core/systems/ExtensionSys.cpp
author David Anderson <dvander@alliedmods.net>
Mon May 26 07:51:11 2008 +0000 (2008-05-26)
changeset 2086 debb2ab2c782
parent 1998 cf2fdf09195a
child 2134 f1d8a7cd3c94
permissions -rw-r--r--
rewrite of the native and dependency binding/interaction code. this will ease the transition for native overrides, and greatly simplifies most of the logic. all native binding code now takes place almost entirely in ShareSys, and PluginSys supplements this logic where appropriate. extensionsys has been cleaned up
ds@1952
     1
/**
ds@1952
     2
 * vim: set ts=4 :
ds@1952
     3
 * =============================================================================
ds@1952
     4
 * SourceMod
ds@1952
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1952
     6
 * =============================================================================
ds@1952
     7
 *
ds@1952
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1952
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1952
    10
 * Free Software Foundation.
ds@1952
    11
 * 
ds@1952
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1952
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1952
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1952
    15
 * details.
ds@1952
    16
 *
ds@1952
    17
 * You should have received a copy of the GNU General Public License along with
ds@1952
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
ds@1952
    19
 *
ds@1952
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1952
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1952
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1952
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1952
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1952
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1952
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1952
    27
 * or <http://www.sourcemod.net/license.php>.
ds@1952
    28
 *
ds@1952
    29
 * Version: $Id$
ds@1952
    30
 */
ds@1952
    31
ds@1952
    32
#include <stdlib.h>
ds@1952
    33
#include "ExtensionSys.h"
ds@1952
    34
#include "LibrarySys.h"
ds@1952
    35
#include "ShareSys.h"
ds@1952
    36
#include "Logger.h"
ds@1952
    37
#include "sourcemm_api.h"
ds@1952
    38
#include "sm_srvcmds.h"
ds@1952
    39
#include "sm_stringutil.h"
ds@1952
    40
ds@1952
    41
CExtensionManager g_Extensions;
ds@1952
    42
IdentityType_t g_ExtType;
ds@1952
    43
ds@1952
    44
void CExtension::Initialize(const char *filename, const char *path)
ds@1952
    45
{
ds@1952
    46
	m_pAPI = NULL;
ds@1952
    47
	m_pIdentToken = NULL;
ds@1952
    48
	unload_code = 0;
ds@1952
    49
	m_bFullyLoaded = false;
ds@1952
    50
	m_File.assign(filename);
ds@1952
    51
	m_Path.assign(path);
ds@1952
    52
}
ds@1952
    53
ds@1952
    54
CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path)
ds@1952
    55
{
ds@1952
    56
	Initialize(filename, path);
ds@1952
    57
	m_pAPI = pAPI;
ds@1952
    58
}
ds@1952
    59
ds@1952
    60
CLocalExtension::CLocalExtension(const char *filename)
ds@1952
    61
{
ds@1952
    62
	m_PlId = 0;
ds@1952
    63
	m_pLib = NULL;
ds@1952
    64
ds@1952
    65
	char path[PLATFORM_MAX_PATH];
ds@1952
    66
ds@1952
    67
	/* First see if there is an engine specific build! */
ds@1952
    68
	g_SourceMod.BuildPath(Path_SM, 
ds@1952
    69
		path, 
ds@1952
    70
		PLATFORM_MAX_PATH, 
ds@1952
    71
#if defined METAMOD_PLAPI_VERSION
ds@1952
    72
#if defined ORANGEBOX_BUILD
ds@1952
    73
		"extensions/auto.2.ep2/%s",
ds@1952
    74
#else
ds@1952
    75
		"extensions/auto.2.ep1/%s",
ds@1952
    76
#endif //ORANGEBOX_BUILD
ds@1952
    77
#else  //METAMOD_PLAPI_VERSION
ds@1952
    78
		"extensions/auto.1.ep1/%s",
ds@1952
    79
#endif //METAMOD_PLAPI_VERSION
ds@1952
    80
		filename);
ds@1952
    81
ds@1952
    82
	/* Try the "normal" version */
ds@1952
    83
	if (!g_LibSys.IsPathFile(path))
ds@1952
    84
	{
ds@1952
    85
		g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename);
ds@1952
    86
	}
ds@1952
    87
ds@1952
    88
	Initialize(filename, path);
ds@1952
    89
}
ds@1952
    90
ds@1952
    91
bool CRemoteExtension::Load(char *error, size_t maxlength)
ds@1952
    92
{
ds@1952
    93
	if (!PerformAPICheck(error, maxlength))
ds@1952
    94
	{
ds@1952
    95
		m_pAPI = NULL;
ds@1952
    96
		return false;
ds@1952
    97
	}
ds@1952
    98
ds@1952
    99
	if (!CExtension::Load(error, maxlength))
ds@1952
   100
	{
ds@1952
   101
		m_pAPI = NULL;
ds@1952
   102
		return false;
ds@1952
   103
	}
ds@1952
   104
ds@1952
   105
	return true;
ds@1952
   106
}
ds@1952
   107
ds@1952
   108
bool CLocalExtension::Load(char *error, size_t maxlength)
ds@1952
   109
{
ds@1952
   110
	m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength);
ds@1952
   111
ds@1952
   112
	if (m_pLib == NULL)
ds@1952
   113
	{
ds@1952
   114
		return false;
ds@1952
   115
	}
ds@1952
   116
ds@1952
   117
	typedef IExtensionInterface *(*GETAPI)();
ds@1952
   118
	GETAPI pfnGetAPI = NULL;
ds@1952
   119
ds@1952
   120
	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
ds@1952
   121
	{
ds@1952
   122
		m_pLib->CloseLibrary();
ds@1952
   123
		m_pLib = NULL;
ds@1952
   124
		snprintf(error, maxlength, "Unable to find extension entry point");
ds@1952
   125
		return false;
ds@1952
   126
	}
ds@1952
   127
ds@1952
   128
	m_pAPI = pfnGetAPI();
ds@1952
   129
ds@1952
   130
	/* Check pointer and version */
ds@1952
   131
	if (!PerformAPICheck(error, maxlength))
ds@1952
   132
	{
ds@1952
   133
		m_pLib->CloseLibrary();
ds@1952
   134
		m_pLib = NULL;
ds@1952
   135
		m_pAPI = NULL;
ds@1952
   136
		return false;
ds@1952
   137
	}
ds@1952
   138
ds@1952
   139
	/* Load as MM:S */
ds@1952
   140
	if (m_pAPI->IsMetamodExtension())
ds@1952
   141
	{
ds@1952
   142
		bool already;
ds@1952
   143
		m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength);
ds@1952
   144
	}
ds@1952
   145
ds@1952
   146
	if (!CExtension::Load(error, maxlength))
ds@1952
   147
	{
ds@1952
   148
		if (m_pAPI->IsMetamodExtension())
ds@1952
   149
		{
ds@1952
   150
			if (m_PlId)
ds@1952
   151
			{
ds@1952
   152
				char dummy[255];
ds@1952
   153
				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
ds@1952
   154
				m_PlId = 0;
ds@1952
   155
			}
ds@1952
   156
		}
ds@1952
   157
		m_pLib->CloseLibrary();
ds@1952
   158
		m_pLib = NULL;
ds@1952
   159
		m_pAPI = NULL;
ds@1952
   160
		return false;
ds@1952
   161
	}
ds@1952
   162
ds@1952
   163
	return true;
ds@1952
   164
}
ds@1952
   165
ds@1952
   166
void CRemoteExtension::Unload()
ds@1952
   167
{
ds@1952
   168
}
ds@1952
   169
ds@1952
   170
void CLocalExtension::Unload()
ds@1952
   171
{
ds@1952
   172
	if (m_pAPI != NULL && m_PlId)
ds@1952
   173
	{
ds@1952
   174
		char temp_buffer[255];
ds@1952
   175
		g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer));
ds@1952
   176
		m_PlId = 0;
ds@1952
   177
	}
ds@1952
   178
ds@1952
   179
	if (m_pLib != NULL)
ds@1952
   180
	{
ds@1952
   181
		m_pLib->CloseLibrary();
ds@1952
   182
		m_pLib = NULL;
ds@1952
   183
	}
ds@1952
   184
}
ds@1952
   185
ds@1952
   186
bool CRemoteExtension::IsExternal()
ds@1952
   187
{
ds@1952
   188
	return true;
ds@1952
   189
}
ds@1952
   190
ds@1952
   191
bool CLocalExtension::IsExternal()
ds@1952
   192
{
ds@1952
   193
	return false;
ds@1952
   194
}
ds@1952
   195
ds@1952
   196
CExtension::~CExtension()
ds@1952
   197
{
ds@1952
   198
	DestroyIdentity();
ds@1952
   199
}
ds@1952
   200
ds@1952
   201
bool CExtension::PerformAPICheck(char *error, size_t maxlength)
ds@1952
   202
{
ds@1952
   203
	if (!m_pAPI)
ds@1952
   204
	{
ds@1952
   205
		snprintf(error, maxlength, "No IExtensionInterface instance provided");
ds@1952
   206
		return false;
ds@1952
   207
	}
ds@1952
   208
	
ds@1952
   209
	if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
ds@1952
   210
	{
ds@1952
   211
		snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
ds@1952
   212
		return false;
ds@1952
   213
	}
ds@1952
   214
ds@1952
   215
	return true;
ds@1952
   216
}
ds@1952
   217
ds@1952
   218
bool CExtension::Load(char *error, size_t maxlength)
ds@1952
   219
{
ds@1952
   220
	CreateIdentity();
ds@1952
   221
	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
ds@1952
   222
	{
ds@1952
   223
		DestroyIdentity();
ds@1952
   224
		return false;
ds@1952
   225
	}
ds@1952
   226
	else
ds@1952
   227
	{
ds@1952
   228
		/* Check if we're past load time */
ds@1952
   229
		if (!g_SourceMod.IsMapLoading())
ds@1952
   230
		{
ds@1952
   231
			m_pAPI->OnExtensionsAllLoaded();
ds@1952
   232
		}
ds@1952
   233
	}
ds@1952
   234
ds@1952
   235
	return true;
ds@1952
   236
}
ds@1952
   237
ds@1952
   238
void CExtension::CreateIdentity()
ds@1952
   239
{
ds@1952
   240
	if (m_pIdentToken != NULL)
ds@1952
   241
	{
ds@1952
   242
		return;
ds@1952
   243
	}
ds@1952
   244
ds@1952
   245
	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
ds@1952
   246
}
ds@1952
   247
ds@1952
   248
void CExtension::DestroyIdentity()
ds@1952
   249
{
ds@1952
   250
	if (m_pIdentToken == NULL)
ds@1952
   251
	{
ds@1952
   252
		return;
ds@1952
   253
	}
ds@1952
   254
ds@1952
   255
	g_ShareSys.DestroyIdentity(m_pIdentToken);
ds@1952
   256
	m_pIdentToken = NULL;
ds@1952
   257
}
ds@1952
   258
ds@1952
   259
void CExtension::MarkAllLoaded()
ds@1952
   260
{
ds@1952
   261
	assert(m_bFullyLoaded == false);
ds@1952
   262
	if (!m_bFullyLoaded)
ds@1952
   263
	{
ds@1952
   264
		m_bFullyLoaded = true;
ds@1952
   265
		m_pAPI->OnExtensionsAllLoaded();
ds@1952
   266
	}
ds@1952
   267
}
ds@1952
   268
dvander@2086
   269
void CExtension::AddPlugin(CPlugin *pPlugin)
ds@1952
   270
{
ds@1952
   271
	/* Unfortunately we have to do this :( */
dvander@2086
   272
	if (m_Dependents.find(pPlugin) != m_Dependents.end())
ds@1952
   273
	{
dvander@2086
   274
		m_Dependents.push_back(pPlugin);
ds@1952
   275
	}
ds@1952
   276
}
ds@1952
   277
ds@1952
   278
void CExtension::SetError(const char *error)
ds@1952
   279
{
ds@1952
   280
	m_Error.assign(error);
ds@1952
   281
}
ds@1952
   282
ds@1952
   283
IExtensionInterface *CExtension::GetAPI()
ds@1952
   284
{
ds@1952
   285
	return m_pAPI;
ds@1952
   286
}
ds@1952
   287
ds@1952
   288
const char *CExtension::GetFilename()
ds@1952
   289
{
ds@1952
   290
	return m_File.c_str();
ds@1952
   291
}
ds@1952
   292
ds@1952
   293
IdentityToken_t *CExtension::GetIdentity()
ds@1952
   294
{
ds@1952
   295
	return m_pIdentToken;
ds@1952
   296
}
ds@1952
   297
ds@1952
   298
bool CRemoteExtension::IsLoaded()
ds@1952
   299
{
ds@1952
   300
	return (m_pAPI != NULL);
ds@1952
   301
}
ds@1952
   302
ds@1952
   303
bool CLocalExtension::IsLoaded()
ds@1952
   304
{
ds@1952
   305
	return (m_pLib != NULL);
ds@1952
   306
}
ds@1952
   307
ds@1952
   308
void CExtension::AddDependency(IfaceInfo *pInfo)
ds@1952
   309
{
ds@1952
   310
	if (m_Deps.find(*pInfo) == m_Deps.end())
ds@1952
   311
	{
ds@1952
   312
		m_Deps.push_back(*pInfo);
ds@1952
   313
	}
ds@1952
   314
}
ds@1952
   315
ds@1952
   316
bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2)
ds@1952
   317
{
ds@1952
   318
	return (i1.iface == i2.iface) && (i1.owner == i2.owner);
ds@1952
   319
}
ds@1952
   320
ds@1952
   321
void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface)
ds@1952
   322
{
ds@1952
   323
	IfaceInfo info;
ds@1952
   324
	info.iface = iface;
ds@1952
   325
	info.owner = pOther;
ds@1952
   326
ds@1952
   327
	List<IfaceInfo>::iterator iter;
ds@1952
   328
	for (iter = m_ChildDeps.begin();
ds@1952
   329
		 iter != m_ChildDeps.end();
ds@1952
   330
		 iter++)
ds@1952
   331
	{
ds@1952
   332
		IfaceInfo &other = (*iter);
ds@1952
   333
		if (other == info)
ds@1952
   334
		{
ds@1952
   335
			return;
ds@1952
   336
		}
ds@1952
   337
	}
ds@1952
   338
ds@1952
   339
	m_ChildDeps.push_back(info);
ds@1952
   340
}
ds@1952
   341
ds@1952
   342
ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
ds@1952
   343
{
ds@1952
   344
	List<IfaceInfo>::iterator iter = m_Deps.begin();
ds@1952
   345
ds@1952
   346
	if (iter == m_Deps.end())
ds@1952
   347
	{
ds@1952
   348
		return NULL;
ds@1952
   349
	}
ds@1952
   350
ds@1952
   351
	if (pOwner)
ds@1952
   352
	{
ds@1952
   353
		*pOwner = (*iter).owner;
ds@1952
   354
	}
ds@1952
   355
	if (pInterface)
ds@1952
   356
	{
ds@1952
   357
		*pInterface = (*iter).iface;
ds@1952
   358
	}
ds@1952
   359
ds@1952
   360
	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
ds@1952
   361
ds@1952
   362
	return (ITERATOR *)pIter;
ds@1952
   363
}
ds@1952
   364
ds@1952
   365
bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
ds@1952
   366
{
ds@1952
   367
	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
ds@1952
   368
	List<IfaceInfo>::iterator _iter;
ds@1952
   369
ds@1952
   370
	if (_iter == m_Deps.end())
ds@1952
   371
	{
ds@1952
   372
		return false;
ds@1952
   373
	}
ds@1952
   374
ds@1952
   375
	_iter++;
ds@1952
   376
ds@1952
   377
	if (pOwner)
ds@1952
   378
	{
ds@1952
   379
		*pOwner = (*_iter).owner;
ds@1952
   380
	}
ds@1952
   381
	if (pInterface)
ds@1952
   382
	{
ds@1952
   383
		*pInterface = (*_iter).iface;
ds@1952
   384
	}
ds@1952
   385
ds@1952
   386
	*pIter = _iter;
ds@1952
   387
ds@1952
   388
	if (_iter == m_Deps.end())
ds@1952
   389
	{
ds@1952
   390
		return false;
ds@1952
   391
	}
ds@1952
   392
ds@1952
   393
	return true;
ds@1952
   394
}
ds@1952
   395
ds@1952
   396
void CExtension::FreeDependencyIterator(ITERATOR *iter)
ds@1952
   397
{
ds@1952
   398
	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
ds@1952
   399
ds@1952
   400
	delete pIter;
ds@1952
   401
}
ds@1952
   402
ds@1952
   403
void CExtension::AddInterface(SMInterface *pInterface)
ds@1952
   404
{
ds@1952
   405
	m_Interfaces.push_back(pInterface);
ds@1952
   406
}
ds@1952
   407
ds@1952
   408
bool CExtension::IsRunning(char *error, size_t maxlength)
ds@1952
   409
{
ds@1952
   410
	if (!IsLoaded())
ds@1952
   411
	{
ds@1952
   412
		if (error)
ds@1952
   413
		{
ds@1952
   414
			snprintf(error, maxlength, "%s", m_Error.c_str());
ds@1952
   415
		}
ds@1952
   416
		return false;
ds@1952
   417
	}
ds@1952
   418
ds@1952
   419
	return m_pAPI->QueryRunning(error, maxlength);
ds@1952
   420
}
ds@1952
   421
ds@1952
   422
void CExtension::AddLibrary(const char *library)
ds@1952
   423
{
ds@1952
   424
	m_Libraries.push_back(library);
ds@1952
   425
}
ds@1952
   426
ds@1952
   427
/*********************
ds@1952
   428
 * EXTENSION MANAGER *
ds@1952
   429
 *********************/
ds@1952
   430
ds@1952
   431
void CExtensionManager::OnSourceModAllInitialized()
ds@1952
   432
{
ds@1952
   433
	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
ds@1952
   434
	g_PluginSys.AddPluginsListener(this);
ds@1952
   435
	g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
ds@1952
   436
	g_ShareSys.AddInterface(NULL, this);
ds@1952
   437
}
ds@1952
   438
ds@1952
   439
void CExtensionManager::OnSourceModShutdown()
ds@1952
   440
{
ds@1952
   441
	g_RootMenu.RemoveRootConsoleCommand("exts", this);
ds@1952
   442
	g_ShareSys.DestroyIdentType(g_ExtType);
ds@1952
   443
}
ds@1952
   444
ds@1952
   445
void CExtensionManager::Shutdown()
ds@1952
   446
{
ds@1952
   447
	List<CExtension *>::iterator iter;
ds@1952
   448
ds@1952
   449
	while ((iter = m_Libs.begin()) != m_Libs.end())
ds@1952
   450
	{
ds@1952
   451
		UnloadExtension((*iter));
ds@1952
   452
	}
ds@1952
   453
}
ds@1952
   454
ds@1952
   455
void CExtensionManager::TryAutoload()
ds@1952
   456
{
ds@1952
   457
	char path[PLATFORM_MAX_PATH];
ds@1952
   458
ds@1952
   459
	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions");
ds@1952
   460
ds@1952
   461
	IDirectory *pDir = g_LibSys.OpenDirectory(path);
ds@1952
   462
	if (!pDir)
ds@1952
   463
	{
ds@1952
   464
		return;
ds@1952
   465
	}
ds@1952
   466
ds@1952
   467
	const char *lfile;
ds@1952
   468
	size_t len;
ds@1952
   469
	while (pDir->MoreFiles())
ds@1952
   470
	{
ds@1952
   471
		if (pDir->IsEntryDirectory())
ds@1952
   472
		{
ds@1952
   473
			pDir->NextEntry();
ds@1952
   474
			continue;
ds@1952
   475
		}
ds@1952
   476
ds@1952
   477
		lfile = pDir->GetEntryName();
ds@1952
   478
		len = strlen(lfile);
ds@1952
   479
		if (len <= 9) /* size of ".autoload" */
ds@1952
   480
		{
ds@1952
   481
			pDir->NextEntry();
ds@1952
   482
			continue;
ds@1952
   483
		}
ds@1952
   484
ds@1952
   485
		if (strcmp(&lfile[len - 9], ".autoload") != 0)
ds@1952
   486
		{
ds@1952
   487
			pDir->NextEntry();
ds@1952
   488
			continue;
ds@1952
   489
		}
ds@1952
   490
ds@1952
   491
		char file[PLATFORM_MAX_PATH];
ds@1952
   492
		len = UTIL_Format(file, sizeof(file), "%s", lfile);
ds@1952
   493
		strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT);
ds@1952
   494
ds@1952
   495
		LoadAutoExtension(file);
ds@1952
   496
		
ds@1952
   497
		pDir->NextEntry();
ds@1952
   498
	}
ds@1952
   499
}
ds@1952
   500
ds@1952
   501
IExtension *CExtensionManager::LoadAutoExtension(const char *path)
ds@1952
   502
{
ds@1952
   503
	if (!strstr(path, "." PLATFORM_LIB_EXT))
ds@1952
   504
	{
ds@1952
   505
		char newpath[PLATFORM_MAX_PATH];
ds@1952
   506
		g_LibSys.PathFormat(newpath, sizeof(newpath), "%s.%s", path, PLATFORM_LIB_EXT);
ds@1952
   507
		return LoadAutoExtension(newpath);
ds@1952
   508
	}
ds@1952
   509
ds@1952
   510
	IExtension *pAlready;
ds@1952
   511
	if ((pAlready=FindExtensionByFile(path)) != NULL)
ds@1952
   512
	{
ds@1952
   513
		return pAlready;
ds@1952
   514
	}
ds@1952
   515
ds@1952
   516
	char error[256];
ds@1952
   517
	CExtension *p = new CLocalExtension(path);
ds@1952
   518
ds@1952
   519
	/* We put us in the list beforehand so extensions that check for each other
ds@1952
   520
	 * won't recursively load each other.
ds@1952
   521
	 */
ds@1952
   522
	m_Libs.push_back(p);
ds@1952
   523
ds@1952
   524
	if (!p->Load(error, sizeof(error)) || !p->IsLoaded())
ds@1952
   525
	{
ds@1952
   526
		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
ds@1952
   527
		p->SetError(error);
ds@1952
   528
	}
ds@1952
   529
ds@1952
   530
	return p;
ds@1952
   531
}
ds@1952
   532
ds@1952
   533
IExtension *CExtensionManager::FindExtensionByFile(const char *file)
ds@1952
   534
{
ds@1952
   535
	List<CExtension *>::iterator iter;
ds@1952
   536
	CExtension *pExt;
ds@1952
   537
ds@1952
   538
	/* Chomp off the path */
ds@1952
   539
	char lookup[PLATFORM_MAX_PATH];
ds@1952
   540
	g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file);
ds@1952
   541
ds@1952
   542
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
ds@1952
   543
	{
ds@1952
   544
		pExt = (*iter);
ds@1952
   545
		char short_file[PLATFORM_MAX_PATH];
ds@1952
   546
		g_LibSys.GetFileFromPath(short_file, sizeof(short_file), pExt->GetFilename());
ds@1952
   547
		if (strcmp(lookup, short_file) == 0)
ds@1952
   548
		{
ds@1952
   549
			return pExt;
ds@1952
   550
		}
ds@1952
   551
	}
ds@1952
   552
ds@1952
   553
	/* If we got no results, test if there was a platform extension.
ds@1952
   554
	 * If not, add one.
ds@1952
   555
	 */
ds@1952
   556
	if (!strstr(file, "." PLATFORM_LIB_EXT))
ds@1952
   557
	{
ds@1952
   558
		char path[PLATFORM_MAX_PATH];
ds@1952
   559
		UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT);
ds@1952
   560
		return FindExtensionByFile(path);
ds@1952
   561
	}
ds@1952
   562
ds@1952
   563
	return NULL;
ds@1952
   564
}
ds@1952
   565
ds@1952
   566
IExtension *CExtensionManager::FindExtensionByName(const char *ext)
ds@1952
   567
{
ds@1952
   568
	List<CExtension *>::iterator iter;
ds@1952
   569
	CExtension *pExt;
ds@1952
   570
	IExtensionInterface *pAPI;
ds@1952
   571
	const char *name;
ds@1952
   572
ds@1952
   573
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
ds@1952
   574
	{
ds@1952
   575
		pExt = (*iter);
ds@1952
   576
		if (!pExt->IsLoaded())
ds@1952
   577
		{
ds@1952
   578
			continue;
ds@1952
   579
		}
ds@1952
   580
		if ((pAPI = pExt->GetAPI()) == NULL)
ds@1952
   581
		{
ds@1952
   582
			continue;
ds@1952
   583
		}
ds@1952
   584
		name = pAPI->GetExtensionName();
ds@1952
   585
		if (!name)
ds@1952
   586
		{
ds@1952
   587
			continue;
ds@1952
   588
		}
ds@1952
   589
		if (strcmp(name, ext) == 0)
ds@1952
   590
		{
ds@1952
   591
			return pExt;
ds@1952
   592
		}
ds@1952
   593
	}
ds@1952
   594
ds@1952
   595
	return NULL;
ds@1952
   596
}
ds@1952
   597
ds@1952
   598
IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength)
ds@1952
   599
{
ds@1952
   600
	IExtension *pAlready;
ds@1952
   601
	if ((pAlready=FindExtensionByFile(file)) != NULL)
ds@1952
   602
	{
ds@1952
   603
		return pAlready;
ds@1952
   604
	}
ds@1952
   605
ds@1952
   606
	CExtension *pExt = new CLocalExtension(file);
ds@1952
   607
ds@1952
   608
	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
ds@1952
   609
	{
ds@1952
   610
		pExt->Unload();
ds@1952
   611
		delete pExt;
ds@1952
   612
		return NULL;
ds@1952
   613
	}
ds@1952
   614
ds@1952
   615
	/* :TODO: do QueryRunning check if the map is loaded */
ds@1952
   616
ds@1952
   617
	m_Libs.push_back(pExt);
ds@1952
   618
ds@1952
   619
	return pExt;
ds@1952
   620
}
ds@1952
   621
ds@1952
   622
void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo)
ds@1952
   623
{
ds@1952
   624
	CExtension *pExt = (CExtension *)pRequester;
ds@1952
   625
	CExtension *pOwner = (CExtension *)pInfo->owner;
ds@1952
   626
ds@1952
   627
	pExt->AddDependency(pInfo);
ds@1952
   628
ds@1952
   629
	IExtensionInterface *pAPI = pExt->GetAPI();
ds@1952
   630
	if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface))
ds@1952
   631
	{
ds@1952
   632
		pOwner->AddChildDependent(pExt, pInfo->iface);
ds@1952
   633
	}
ds@1952
   634
}
ds@1952
   635
ds@1952
   636
void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
ds@1952
   637
{
ds@1952
   638
	CExtension *pExt = (CExtension *)pOwner;
ds@1952
   639
ds@1952
   640
	pExt->AddInterface(pInterface);
ds@1952
   641
}
ds@1952
   642
dvander@2086
   643
void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin )
ds@1952
   644
{
ds@1952
   645
	CExtension *pExt = (CExtension *)pParent;
ds@1952
   646
ds@1952
   647
	pExt->AddPlugin(pPlugin);
ds@1952
   648
}
ds@1952
   649
ds@1952
   650
void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
ds@1952
   651
{
ds@1952
   652
	List<CExtension *>::iterator iter;
ds@1952
   653
ds@1952
   654
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
ds@1952
   655
	{
dvander@2086
   656
		(*iter)->DropRefsTo((CPlugin *)plugin);
ds@1952
   657
	}
ds@1952
   658
}
ds@1952
   659
ds@1952
   660
CExtension *CExtensionManager::FindByOrder(unsigned int num)
ds@1952
   661
{
ds@1952
   662
	if (num < 1 || num > m_Libs.size())
ds@1952
   663
	{
ds@1952
   664
		return NULL;
ds@1952
   665
	}
ds@1952
   666
ds@1952
   667
	List<CExtension *>::iterator iter = m_Libs.begin();
ds@1952
   668
ds@1952
   669
	while (iter != m_Libs.end())
ds@1952
   670
	{
ds@1952
   671
		if (--num == 0)
ds@1952
   672
		{
ds@1952
   673
			return (*iter);
ds@1952
   674
		}
ds@1952
   675
		iter++;
ds@1952
   676
	}
ds@1952
   677
ds@1952
   678
	return NULL;
ds@1952
   679
}
ds@1952
   680
ds@1952
   681
bool CExtensionManager::UnloadExtension(IExtension *_pExt)
ds@1952
   682
{
ds@1952
   683
	if (!_pExt)
ds@1952
   684
	{
ds@1952
   685
		return false;
ds@1952
   686
	}
ds@1952
   687
ds@1952
   688
	CExtension *pExt = (CExtension *)_pExt;
ds@1952
   689
ds@1952
   690
	if (m_Libs.find(pExt) == m_Libs.end())
ds@1952
   691
	{
ds@1952
   692
		return false;
ds@1952
   693
	}
ds@1952
   694
ds@1952
   695
	/* First remove us from internal lists */
ds@1952
   696
	g_ShareSys.RemoveInterfaces(_pExt);
ds@1952
   697
	m_Libs.remove(pExt);
ds@1952
   698
ds@1952
   699
	List<CExtension *> UnloadQueue;
ds@1952
   700
ds@1952
   701
	/* Handle dependencies */
ds@1952
   702
	if (pExt->IsLoaded())
ds@1952
   703
	{
ds@1952
   704
		/* Unload any dependent plugins */
dvander@2086
   705
		List<CPlugin *>::iterator p_iter = pExt->m_Dependents.begin();
dvander@2086
   706
		while (p_iter != pExt->m_Dependents.end())
ds@1952
   707
		{
ds@1952
   708
			/* We have to manually unlink ourselves here, since we're no longer being managed */
ds@1952
   709
			g_PluginSys.UnloadPlugin((*p_iter));
dvander@2086
   710
			p_iter = pExt->m_Dependents.erase(p_iter);
ds@1952
   711
		}
ds@1952
   712
ds@1952
   713
		List<String>::iterator s_iter;
ds@1952
   714
		for (s_iter = pExt->m_Libraries.begin();
ds@1952
   715
			 s_iter != pExt->m_Libraries.end();
ds@1952
   716
			 s_iter++)
ds@1952
   717
		{
ds@1952
   718
			g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true);
ds@1952
   719
		}
ds@1952
   720
ds@1952
   721
		/* Notify and/or unload all dependencies */
ds@1952
   722
		List<CExtension *>::iterator c_iter;
ds@1952
   723
		CExtension *pDep;
ds@1952
   724
		IExtensionInterface *pAPI;
ds@1952
   725
		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
ds@1952
   726
		{
ds@1952
   727
			pDep = (*c_iter);
ds@1952
   728
			if ((pAPI=pDep->GetAPI()) == NULL)
ds@1952
   729
			{
ds@1952
   730
				continue;
ds@1952
   731
			}
ds@1952
   732
			if (pDep == pExt)
ds@1952
   733
			{
ds@1952
   734
				continue;
ds@1952
   735
			}
ds@1952
   736
			/* Now, get its dependency list */
ds@1952
   737
			bool dropped = false;
ds@1952
   738
			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
ds@1952
   739
			while (i_iter != pDep->m_Deps.end())
ds@1952
   740
			{
ds@1952
   741
				if ((*i_iter).owner == _pExt)
ds@1952
   742
				{
ds@1952
   743
					if (!pAPI->QueryInterfaceDrop((*i_iter).iface))
ds@1952
   744
					{
ds@1952
   745
						if (!dropped)
ds@1952
   746
						{
ds@1952
   747
							dropped = true;
ds@1952
   748
							UnloadQueue.push_back(pDep);
ds@1952
   749
						}
ds@1952
   750
					}
ds@1952
   751
					pAPI->NotifyInterfaceDrop((*i_iter).iface);
ds@1952
   752
					i_iter = pDep->m_Deps.erase(i_iter);
ds@1952
   753
				}
ds@1952
   754
				else
ds@1952
   755
				{
ds@1952
   756
					i_iter++;
ds@1952
   757
				}
ds@1952
   758
			}
ds@1952
   759
			/* Flush out any back references to this plugin */
ds@1952
   760
			i_iter = pDep->m_ChildDeps.begin();
ds@1952
   761
			while (i_iter != pDep->m_ChildDeps.end())
ds@1952
   762
			{
ds@1952
   763
				if ((*i_iter).owner == pExt)
ds@1952
   764
				{
ds@1952
   765
					i_iter = pDep->m_ChildDeps.erase(i_iter);
ds@1952
   766
				}
ds@1952
   767
				else
ds@1952
   768
				{
ds@1952
   769
					i_iter++;
ds@1952
   770
				}
ds@1952
   771
			}
ds@1952
   772
		}
ds@1952
   773
ds@1952
   774
		/* Unbind our natives from Core */
dvander@2086
   775
		pExt->DropEverything();
ds@1952
   776
ds@1952
   777
		/* Tell it to unload */
ds@1952
   778
		pAPI = pExt->GetAPI();
ds@1952
   779
		pAPI->OnExtensionUnload();
ds@1952
   780
	}
ds@1952
   781
ds@1952
   782
	IdentityToken_t *pIdentity;
ds@1952
   783
	if ((pIdentity = pExt->GetIdentity()) != NULL)
ds@1952
   784
	{
ds@1952
   785
		SMGlobalClass *glob = SMGlobalClass::head;
ds@1952
   786
		while (glob)
ds@1952
   787
		{
ds@1952
   788
			glob->OnSourceModIdentityDropped(pIdentity);
ds@1952
   789
			glob = glob->m_pGlobalClassNext;
ds@1952
   790
		}
ds@1952
   791
	}
ds@1952
   792
ds@1952
   793
	pExt->Unload();
ds@1952
   794
	delete pExt;
ds@1952
   795
ds@1952
   796
	List<CExtension *>::iterator iter;
ds@1952
   797
	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
ds@1952
   798
	{
ds@1952
   799
		/* NOTE: This is safe because the unload function backs out of anything not present */
ds@1952
   800
		UnloadExtension((*iter));
ds@1952
   801
	}
ds@1952
   802
ds@1952
   803
	return true;
ds@1952
   804
}
ds@1952
   805
ds@1952
   806
void CExtensionManager::MarkAllLoaded()
ds@1952
   807
{
ds@1952
   808
	List<CExtension *>::iterator iter;
ds@1952
   809
	CExtension *pExt;
ds@1952
   810
ds@1952
   811
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
ds@1952
   812
	{
ds@1952
   813
		pExt = (*iter);
ds@1952
   814
		if (!pExt->IsLoaded())
ds@1952
   815
		{
ds@1952
   816
			continue;
ds@1952
   817
		}
ds@1952
   818
		if (pExt->m_bFullyLoaded)
ds@1952
   819
		{
ds@1952
   820
			continue;
ds@1952
   821
		}
ds@1952
   822
		pExt->MarkAllLoaded();
ds@1952
   823
	}
ds@1952
   824
}
ds@1952
   825
ds@1952
   826
void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload)
ds@1952
   827
{
ds@1952
   828
	/* This function doesn't really need to do anything now.  We make sure the 
ds@1952
   829
	 * other extension is loaded, but handling of dependencies is really done 
ds@1952
   830
	 * by the interface fetcher.
ds@1952
   831
	 */
ds@1952
   832
	if (required || autoload)
ds@1952
   833
	{
ds@1952
   834
		LoadAutoExtension(file);
ds@1952
   835
	}
ds@1952
   836
}
ds@1952
   837
ds@1952
   838
void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
ds@1952
   839
{
ds@1952
   840
	int argcount = command.ArgC();
ds@1952
   841
	if (argcount >= 3)
ds@1952
   842
	{
ds@1952
   843
		const char *cmd = command.Arg(2);
ds@1952
   844
		if (strcmp(cmd, "list") == 0)
ds@1952
   845
		{
ds@1952
   846
			List<CExtension *>::iterator iter;
ds@1952
   847
			CExtension *pExt;
ds@1952
   848
			unsigned int num = 1;
ds@1952
   849
			switch (m_Libs.size())
ds@1952
   850
			{
ds@1952
   851
			case 1:
ds@1952
   852
				{
ds@1952
   853
					g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
ds@1952
   854
					break;
ds@1952
   855
				}
ds@1952
   856
			case 0:
ds@1952
   857
				{
ds@1952
   858
					g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
ds@1952
   859
					break;
ds@1952
   860
				}
ds@1952
   861
			default:
ds@1952
   862
				{
ds@1952
   863
					g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
ds@1952
   864
					break;
ds@1952
   865
				}
ds@1952
   866
			}
ds@1952
   867
			for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
ds@1952
   868
			{
ds@1952
   869
				pExt = (*iter);
ds@1952
   870
				if (pExt->IsLoaded())
ds@1952
   871
				{
ds@1952
   872
					char error[255];
ds@1952
   873
					if (!pExt->IsRunning(error, sizeof(error)))
ds@1952
   874
					{
ds@1952
   875
						g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
ds@1952
   876
					}
ds@1952
   877
					else
ds@1952
   878
					{
ds@1952
   879
						IExtensionInterface *pAPI = pExt->GetAPI();
ds@1952
   880
						const char *name = pAPI->GetExtensionName();
ds@1952
   881
						const char *version = pAPI->GetExtensionVerString();
ds@1952
   882
						const char *descr = pAPI->GetExtensionDescription();
ds@1952
   883
						g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
ds@1952
   884
					}
ds@1952
   885
				} else {
ds@1952
   886
					g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
ds@1952
   887
				}
ds@1952
   888
			}
ds@1952
   889
			return;
ds@1952
   890
		}
ds@1952
   891
		else if (strcmp(cmd, "info") == 0)
ds@1952
   892
		{
ds@1952
   893
			if (argcount < 4)
ds@1952
   894
			{
ds@1952
   895
				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
ds@1952
   896
				return;
ds@1952
   897
			}
ds@1952
   898
ds@1952
   899
			const char *sId = command.Arg(3);
ds@1952
   900
			unsigned int id = atoi(sId);
ds@1952
   901
			if (id <= 0)
ds@1952
   902
			{
ds@1952
   903
				g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
ds@1952
   904
				return;
ds@1952
   905
			}
ds@1952
   906
ds@1952
   907
			if (m_Libs.size() == 0)
ds@1952
   908
			{
ds@1952
   909
				g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
ds@1952
   910
				return;
ds@1952
   911
			}
ds@1952
   912
ds@1952
   913
			if (id > m_Libs.size())
ds@1952
   914
			{
ds@1952
   915
				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
ds@1952
   916
				return;
ds@1952
   917
			}
ds@1952
   918
ds@1952
   919
			List<CExtension *>::iterator iter = m_Libs.begin();
ds@1952
   920
			CExtension *pExt = NULL;
ds@1952
   921
			while (iter != m_Libs.end())
ds@1952
   922
			{
ds@1952
   923
				if (--id == 0)
ds@1952
   924
				{
ds@1952
   925
					pExt = (*iter);
ds@1952
   926
					break;
ds@1952
   927
				}
ds@1952
   928
				iter++;
ds@1952
   929
			}
ds@1952
   930
			/* This should never happen */
ds@1952
   931
			if (!pExt)
ds@1952
   932
			{
ds@1952
   933
				g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
ds@1952
   934
				return;
ds@1952
   935
			}
ds@1952
   936
ds@1952
   937
			if (!pExt->IsLoaded())
ds@1952
   938
			{
ds@1952
   939
				g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
ds@1952
   940
				g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
ds@1952
   941
			}
ds@1952
   942
			else
ds@1952
   943
			{
ds@1952
   944
				char error[255];
ds@1952
   945
				if (!pExt->IsRunning(error, sizeof(error)))
ds@1952
   946
				{
ds@1952
   947
					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
ds@1952
   948
					g_RootMenu.ConsolePrint(" Loaded: Yes");
ds@1952
   949
					g_RootMenu.ConsolePrint(" Running: No (%s)", error);
ds@1952
   950
				}
ds@1952
   951
				else
ds@1952
   952
				{
ds@1952
   953
					IExtensionInterface *pAPI = pExt->GetAPI();
ds@1952
   954
					g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
ds@1952
   955
					g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
ds@1952
   956
					g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
ds@1952
   957
					g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
ds@1952
   958
					g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
ds@1952
   959
ds@1952
   960
					if (pExt->IsExternal())
ds@1952
   961
					{
ds@1952
   962
						g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod");
ds@1952
   963
					}
ds@1952
   964
					else if (pAPI->IsMetamodExtension())
ds@1952
   965
					{
ds@1952
   966
						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source");
ds@1952
   967
					}
ds@1952
   968
					else
ds@1952
   969
					{
ds@1952
   970
						g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod");
ds@1952
   971
					}
ds@1952
   972
				}
ds@1952
   973
			}
ds@1952
   974
			return;
ds@1952
   975
		}
ds@1952
   976
		else if (strcmp(cmd, "unload") == 0)
ds@1952
   977
		{
ds@1952
   978
			if (argcount < 4)
ds@1952
   979
			{
ds@1952
   980
				g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
ds@1952
   981
				return;
ds@1952
   982
			}
ds@1952
   983
ds@1952
   984
			const char *arg = command.Arg(3);
ds@1952
   985
			unsigned int num = atoi(arg);
ds@1952
   986
			CExtension *pExt = FindByOrder(num);
ds@1952
   987
ds@1952
   988
			if (!pExt)
ds@1952
   989
			{
ds@1952
   990
				g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
ds@1952
   991
				return;
ds@1952
   992
			}
ds@1952
   993
ds@1952
   994
			if (argcount > 4 && pExt->unload_code)
ds@1952
   995
			{
ds@1952
   996
				const char *unload = command.Arg(4);
ds@1952
   997
				if (pExt->unload_code == (unsigned)atoi(unload))
ds@1952
   998
				{
ds@1952
   999
					char filename[PLATFORM_MAX_PATH];
ds@1952
  1000
					snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
ds@1952
  1001
					UnloadExtension(pExt);
ds@1952
  1002
					g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
ds@1952
  1003
				}
ds@1952
  1004
				else
ds@1952
  1005
				{
ds@1952
  1006
					g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
ds@1952
  1007
				}
ds@1952
  1008
				return;
ds@1952
  1009
			}
ds@1952
  1010
ds@1952
  1011
			if (!pExt->IsLoaded() 
dvander@2086
  1012
				|| (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size()))
ds@1952
  1013
			{
ds@1952
  1014
				char filename[PLATFORM_MAX_PATH];
ds@1952
  1015
				snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
ds@1952
  1016
				UnloadExtension(pExt);
ds@1952
  1017
				g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
ds@1952
  1018
				return;
ds@1952
  1019
			}
ds@1952
  1020
			else
ds@1952
  1021
			{
dvander@2086
  1022
				List<CPlugin *> plugins;
ds@1952
  1023
				if (pExt->m_ChildDeps.size())
ds@1952
  1024
				{
ds@1952
  1025
					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
ds@1952
  1026
					List<CExtension *>::iterator iter;
ds@1952
  1027
					CExtension *pOther;
ds@1952
  1028
					/* Get list of all extensions */
ds@1952
  1029
					for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
ds@1952
  1030
					{
ds@1952
  1031
						List<IfaceInfo>::iterator i_iter;
ds@1952
  1032
						pOther = (*iter);
ds@1952
  1033
						if (!pOther->IsLoaded() || pOther == pExt)
ds@1952
  1034
						{
ds@1952
  1035
							continue;
ds@1952
  1036
						}
ds@1952
  1037
						/* Get their dependencies */
ds@1952
  1038
						for (i_iter=pOther->m_Deps.begin();
ds@1952
  1039
							 i_iter!=pOther->m_Deps.end();
ds@1952
  1040
							 i_iter++)
ds@1952
  1041
						{
ds@1952
  1042
							/* Is this dependency to us? */
ds@1952
  1043
							if ((*i_iter).owner != pExt)
ds@1952
  1044
							{
ds@1952
  1045
								continue;
ds@1952
  1046
							}
ds@1952
  1047
							/* Will our dependent care? */
ds@1952
  1048
							if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
ds@1952
  1049
							{
ds@1952
  1050
								g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
ds@1952
  1051
								/* Add to plugin unload list */
dvander@2086
  1052
								List<CPlugin *>::iterator p_iter;
dvander@2086
  1053
								for (p_iter=pOther->m_Dependents.begin();
dvander@2086
  1054
									 p_iter!=pOther->m_Dependents.end();
ds@1952
  1055
									 p_iter++)
ds@1952
  1056
								{
ds@1952
  1057
									if (plugins.find((*p_iter)) == plugins.end())
ds@1952
  1058
									{
ds@1952
  1059
										plugins.push_back((*p_iter));
ds@1952
  1060
									}
ds@1952
  1061
								}
ds@1952
  1062
							}
ds@1952
  1063
						}
ds@1952
  1064
					}
ds@1952
  1065
				}
dvander@2086
  1066
				if (pExt->m_Dependents.size())
ds@1952
  1067
				{
ds@1952
  1068
					g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
dvander@2086
  1069
					List<CPlugin *>::iterator iter;
dvander@2086
  1070
					CPlugin *pPlugin;
dvander@2086
  1071
					for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++)
ds@1952
  1072
					{
ds@1952
  1073
						pPlugin = (*iter);
ds@1952
  1074
						if (plugins.find(pPlugin) == plugins.end())
ds@1952
  1075
						{
ds@1952
  1076
							plugins.push_back(pPlugin);
ds@1952
  1077
						}
ds@1952
  1078
					}
ds@1952
  1079
					for (iter = plugins.begin(); iter != plugins.end(); iter++)
ds@1952
  1080
					{
ds@1952
  1081
						pPlugin = (*iter);
ds@1952
  1082
						g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
ds@1952
  1083
					}
ds@1952
  1084
				}
ds@1952
  1085
				srand(static_cast<int>(time(NULL)));
ds@1952
  1086
				pExt->unload_code = (rand() % 877) + 123;	//123 to 999
ds@1952
  1087
				g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
ds@1952
  1088
				g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
ds@1952
  1089
ds@1952
  1090
				return;
ds@1952
  1091
			}
ds@1952
  1092
		}
ds@1952
  1093
	}
ds@1952
  1094
ds@1952
  1095
	g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
ds@1952
  1096
	g_RootMenu.DrawGenericOption("info", "Extra extension information");
ds@1952
  1097
	g_RootMenu.DrawGenericOption("list", "List extensions");
ds@1952
  1098
	g_RootMenu.DrawGenericOption("unload", "Unload an extension");
ds@1952
  1099
}
ds@1952
  1100
ds@1952
  1101
CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
ds@1952
  1102
{
ds@1952
  1103
	if (ptr->type == g_ExtType)
ds@1952
  1104
	{
ds@1952
  1105
		return (CExtension *)(ptr->ptr);
ds@1952
  1106
	}
ds@1952
  1107
ds@1952
  1108
	return NULL;
ds@1952
  1109
}
ds@1952
  1110
ds@1952
  1111
CExtensionManager::CExtensionManager()
ds@1952
  1112
{
ds@1952
  1113
}
ds@1952
  1114
ds@1952
  1115
CExtensionManager::~CExtensionManager()
ds@1952
  1116
{
ds@1952
  1117
}
ds@1952
  1118
ds@1952
  1119
void CExtensionManager::AddLibrary(IExtension *pSource, const char *library)
ds@1952
  1120
{
ds@1952
  1121
	CExtension *pExt = (CExtension *)pSource;
ds@1952
  1122
	pExt->AddLibrary(library);
ds@1952
  1123
	g_PluginSys.OnLibraryAction(library, false, false);
ds@1952
  1124
}
ds@1952
  1125
ds@1952
  1126
bool CExtensionManager::LibraryExists(const char *library)
ds@1952
  1127
{
ds@1952
  1128
	CExtension *pExt;
ds@1952
  1129
ds@1952
  1130
	for (List<CExtension *>::iterator iter = m_Libs.begin();
ds@1952
  1131
		 iter != m_Libs.end();
ds@1952
  1132
		 iter++)
ds@1952
  1133
	{
ds@1952
  1134
		pExt = (*iter);
ds@1952
  1135
		for (List<String>::iterator s_iter = pExt->m_Libraries.begin();
ds@1952
  1136
			 s_iter != pExt->m_Libraries.end();
ds@1952
  1137
			 s_iter++)
ds@1952
  1138
		{
ds@1952
  1139
			if ((*s_iter).compare(library) == 0)
ds@1952
  1140
			{
ds@1952
  1141
				return true;
ds@1952
  1142
			}
ds@1952
  1143
		}
ds@1952
  1144
	}
ds@1952
  1145
ds@1952
  1146
	return false;
ds@1952
  1147
}
ds@1952
  1148
ds@1952
  1149
IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface,
ds@1952
  1150
											const char *filepath,
ds@1952
  1151
											const char *filename,
ds@1952
  1152
											char *error,
ds@1952
  1153
											size_t maxlength)
ds@1952
  1154
{
ds@1952
  1155
	IExtension *pAlready;
ds@1952
  1156
	if ((pAlready=FindExtensionByFile(filename)) != NULL)
ds@1952
  1157
	{
ds@1952
  1158
		return pAlready;
ds@1952
  1159
	}
ds@1952
  1160
ds@1952
  1161
	CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath);
ds@1952
  1162
ds@1952
  1163
	if (!pExt->Load(error, maxlength) || !pExt->IsLoaded())
ds@1952
  1164
	{
ds@1952
  1165
		pExt->Unload();
ds@1952
  1166
		delete pExt;
ds@1952
  1167
		return NULL;
ds@1952
  1168
	}
ds@1952
  1169
ds@1952
  1170
	m_Libs.push_back(pExt);
ds@1952
  1171
ds@1952
  1172
	return pExt;
ds@1952
  1173
}
ds@1952
  1174
faluco@1993
  1175
void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
faluco@1993
  1176
{
dvander@1998
  1177
	IExtensionInterface *pAPI;
faluco@1993
  1178
	List<CExtension *>::iterator iter;
faluco@1993
  1179
faluco@1993
  1180
	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
faluco@1993
  1181
	{
dvander@1998
  1182
		if ((pAPI = (*iter)->GetAPI()) == NULL)
faluco@1993
  1183
		{
dvander@1998
  1184
			continue;
dvander@1998
  1185
		}
dvander@1998
  1186
		if (pAPI->GetExtensionVersion() > 3)
dvander@1998
  1187
		{
dvander@1998
  1188
			pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax);
faluco@1993
  1189
		}
faluco@1993
  1190
	}
faluco@1993
  1191
}