core/systems/PluginSys.cpp
author David Anderson <dvander@alliedmods.net>
Sat Dec 16 22:31:32 2006 +0000 (2006-12-16)
changeset 220 73d3fcf9bbd1
parent 216 38b1bdbc5bc9
child 222 8d009d0087f1
permissions -rw-r--r--
these should compile now
     1 #include <stdio.h>
     2 #include "PluginSys.h"
     3 #include "LibrarySys.h"
     4 #include "HandleSys.h"
     5 #include "sourcemm_api.h"
     6 #include "sourcemod.h"
     7 #include "CTextParsers.h"
     8 
     9 CPluginManager g_PluginSys;
    10 HandleType_t g_PluginType = 0;
    11 
    12 CPlugin::CPlugin(const char *file)
    13 {
    14 	static int MySerial = 0;
    15 
    16 	m_type = PluginType_Private;
    17 	m_status = Plugin_Uncompiled;
    18 	m_serial = ++MySerial;
    19 	m_plugin = NULL;
    20 	m_funcsnum = 0;
    21 	m_priv_funcs = NULL;
    22 	m_pub_funcs = NULL;
    23 	m_errormsg[256] = '\0';
    24 	snprintf(m_filename, sizeof(m_filename), "%s", file);
    25 	/* :TODO: ShareSys token */
    26 	m_handle = g_HandleSys.CreateHandle(g_PluginType, this, DEFAULT_IDENTITY, 1);
    27 }
    28 
    29 CPlugin::~CPlugin()
    30 {
    31 	if (m_ctx.base)
    32 	{
    33 		g_pSourcePawn->FreeBaseContext(m_ctx.base);
    34 		m_ctx.base = NULL;
    35 	}
    36 	if (m_ctx.ctx)
    37 	{
    38 		m_ctx.vm->FreeContext(m_ctx.ctx);
    39 		m_ctx.ctx = NULL;
    40 	}
    41 	if (m_ctx.co)
    42 	{
    43 		m_ctx.vm->AbortCompilation(m_ctx.co);
    44 		m_ctx.co = NULL;
    45 	}
    46 
    47 	if (m_pub_funcs)
    48 	{
    49 		for (uint32_t i=0; i<m_plugin->info.publics_num; i++)
    50 		{
    51 			g_PluginSys.ReleaseFunctionToPool(m_pub_funcs[i]);
    52 		}
    53 		delete [] m_pub_funcs;
    54 		m_pub_funcs = NULL;
    55 	}
    56 
    57 	if (m_plugin)
    58 	{
    59 		g_pSourcePawn->FreeFromMemory(m_plugin);
    60 		m_plugin = NULL;
    61 	}
    62 
    63 	if (m_priv_funcs)
    64 	{
    65 		for (unsigned int i=0; i<m_funcsnum; i++)
    66 		{
    67 			g_PluginSys.ReleaseFunctionToPool(m_priv_funcs[i]);
    68 		}
    69 		delete [] m_priv_funcs;
    70 		m_priv_funcs = NULL;
    71 	}
    72 }
    73 
    74 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
    75 {
    76 	char fullpath[PLATFORM_MAX_PATH+1];
    77 	g_LibSys.PathFormat(fullpath, sizeof(fullpath), "%s/plugins/%s", g_SourceMod.GetSMBaseDir(), file);
    78 	FILE *fp = fopen(fullpath, "rb");
    79 
    80 	if (!fp)
    81 	{
    82 		if (error)
    83 		{
    84 			snprintf(error, maxlength, "Unable to open file");
    85 			return NULL;
    86 		} else {
    87 			CPlugin *pPlugin = new CPlugin(file);
    88 			snprintf(pPlugin->m_errormsg, sizeof(pPlugin->m_errormsg), "Unable to open file");
    89 			pPlugin->m_status = Plugin_BadLoad;
    90 			return pPlugin;
    91 		}
    92 	}
    93 
    94 	int err;
    95 	sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
    96 	if (pl == NULL)
    97 	{
    98 		fclose(fp);
    99 		if (error)
   100 		{
   101 			snprintf(error, maxlength, "Error %d while parsing plugin", err);
   102 			return NULL;
   103 		} else {
   104 			CPlugin *pPlugin = new CPlugin(file);
   105 			snprintf(pPlugin->m_errormsg, sizeof(pPlugin->m_errormsg), "Error %d while parsing plugin", err);
   106 			pPlugin->m_status = Plugin_BadLoad;
   107 			return pPlugin;
   108 		}
   109 	}
   110 
   111 	fclose(fp);
   112 
   113 	CPlugin *pPlugin = new CPlugin(file);
   114 	pPlugin->m_plugin = pl;
   115 	return pPlugin;
   116 }
   117 
   118 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
   119 {
   120 	if (!m_plugin)
   121 	{
   122 		return NULL;
   123 	}
   124 
   125 	/* :NOTICE: We will eventually need to change these natives
   126 	 * for swapping in new contexts
   127 	 */
   128 	if (m_ctx.co || m_ctx.ctx)
   129 	{
   130 		return NULL;
   131 	}
   132 
   133 	m_status = Plugin_Uncompiled;
   134 
   135 	m_ctx.vm = vm;
   136 	m_ctx.co = vm->StartCompilation(m_plugin);
   137 
   138 	return m_ctx.co;
   139 }
   140 
   141 void CPlugin::CancelMyCompile()
   142 {
   143 	if (!m_ctx.co)
   144 	{
   145 		return;
   146 	}
   147 
   148 	m_ctx.vm->AbortCompilation(m_ctx.co);
   149 	m_ctx.co = NULL;
   150 	m_ctx.vm = NULL;
   151 }
   152 
   153 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
   154 {
   155 	if (!m_ctx.co)
   156 	{
   157 		return false;
   158 	}
   159 
   160 	int err;
   161 	m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
   162 	if (!m_ctx.ctx)
   163 	{
   164 		memset(&m_ctx, 0, sizeof(m_ctx));
   165 		if (!error)
   166 		{
   167 			SetErrorState(Plugin_Error, "Failed to compile (error %d)", err);
   168 		} else {
   169 			snprintf(error, maxlength, "Failed to compile (error %d)", err);
   170 		}
   171 		return false;
   172 	}
   173 
   174 	m_ctx.base = g_pSourcePawn->CreateBaseContext(m_ctx.ctx);
   175 	m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
   176 
   177 	m_funcsnum = m_ctx.vm->FunctionCount(m_ctx.ctx);
   178 
   179 	/**
   180 	 * Note: Since the m_plugin member will never change,
   181 	 * it is safe to assume the function count will never change
   182 	 */
   183 	if (m_funcsnum && m_priv_funcs == NULL)
   184 	{
   185 		m_priv_funcs = new CFunction *[m_funcsnum];
   186 		memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
   187 	} else {
   188 		m_priv_funcs = NULL;
   189 	}
   190 
   191 	if (m_plugin->info.publics_num && m_pub_funcs == NULL)
   192 	{
   193 		m_pub_funcs = new CFunction *[m_plugin->info.publics_num];
   194 		memset(m_pub_funcs, 0, sizeof(CFunction *) * m_plugin->info.publics_num);
   195 	} else {
   196 		m_pub_funcs = NULL;
   197 	}
   198 
   199 	m_status = Plugin_Created;
   200 	m_ctx.co = NULL;
   201 
   202 	UpdateInfo();
   203 
   204 	return true;
   205 }
   206 
   207 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
   208 {
   209 	m_status = status;
   210 
   211 	va_list ap;
   212 	va_start(ap, error_fmt);
   213 	vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
   214 	va_end(ap);
   215 }
   216 
   217 IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
   218 {
   219 	CFunction *pFunc = NULL;
   220 	funcid_t save = func_id;
   221 
   222 	if (func_id & 1)
   223 	{
   224 		func_id >>= 1;
   225 		if (func_id >= m_plugin->info.publics_num)
   226 		{
   227 			return NULL;
   228 		}
   229 		pFunc = m_pub_funcs[func_id];
   230 		if (!pFunc)
   231 		{
   232 			pFunc = g_PluginSys.GetFunctionFromPool(save, this);
   233 			m_pub_funcs[func_id] = pFunc;
   234 		}
   235 	} else {
   236 		func_id >>= 1;
   237 		unsigned int index;
   238 		if (!g_pVM->FunctionLookup(m_ctx.ctx, func_id, &index))
   239 		{
   240 			return NULL;
   241 		}
   242 		pFunc = m_priv_funcs[func_id];
   243 		if (!pFunc)
   244 		{
   245 			pFunc = g_PluginSys.GetFunctionFromPool(save, this);
   246 			m_priv_funcs[func_id] = pFunc;
   247 		}
   248 	}
   249 
   250 	return pFunc;
   251 }
   252 
   253 IPluginFunction *CPlugin::GetFunctionByName(const char *public_name)
   254 {
   255 	uint32_t index;
   256 	IPluginContext *base = m_ctx.base;
   257 
   258 	if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE)
   259 	{
   260 		return NULL;
   261 	}
   262 
   263 	CFunction *pFunc = m_pub_funcs[index];
   264 	if (!pFunc)
   265 	{
   266 		sp_public_t *pub = NULL;
   267 		base->GetPublicByIndex(index, &pub);
   268 		if (pub)
   269 		{
   270 			pFunc = g_PluginSys.GetFunctionFromPool(pub->funcid, this);
   271 			m_pub_funcs[index] = pFunc;
   272 		}
   273 	}
   274 
   275 	return pFunc;
   276 }
   277 
   278 void CPlugin::UpdateInfo()
   279 {
   280 	/* Now grab the info */
   281 	uint32_t idx;
   282 	IPluginContext *base = GetBaseContext();
   283 	int err = base->FindPubvarByName("myinfo", &idx);
   284 
   285 	if (err == SP_ERROR_NONE)
   286 	{
   287 		struct sm_plugininfo_c_t
   288 		{
   289 			cell_t name;
   290 			cell_t description;
   291 			cell_t author;
   292 			cell_t version;
   293 			cell_t url;
   294 		};
   295 		sm_plugininfo_c_t *cinfo;
   296 		cell_t local_addr;
   297 
   298 		base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
   299 		base->LocalToString(cinfo->name, (char **)&m_info.name);
   300 		base->LocalToString(cinfo->description, (char **)&m_info.description);
   301 		base->LocalToString(cinfo->author, (char **)&m_info.author);
   302 		base->LocalToString(cinfo->url, (char **)&m_info.url);
   303 		base->LocalToString(cinfo->version, (char **)&m_info.version);
   304 	}
   305 
   306 	m_info.author = m_info.author ? m_info.author : "";
   307 	m_info.description = m_info.description ? m_info.description : "";
   308 	m_info.name = m_info.name ? m_info.name : "";
   309 	m_info.url = m_info.url ? m_info.url : "";
   310 	m_info.version = m_info.version ? m_info.version : "";
   311 }
   312 
   313 void CPlugin::Call_OnPluginInit()
   314 {
   315 	if (m_status != Plugin_Loaded)
   316 	{
   317 		return;
   318 	}
   319 
   320 	m_status = Plugin_Running;
   321 
   322 	int err;
   323 	cell_t result;
   324 	IPluginFunction *pFunction = GetFunctionByName("OnPluginInit");
   325 	if (!pFunction)
   326 	{
   327 		return;
   328 	}
   329 
   330 	/* :TODO: push our own handle */
   331 	pFunction->PushCell(0);
   332 	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   333 	{
   334 		/* :TODO: log into debugger instead */
   335 		SetErrorState(Plugin_Error, "Runtime error %d", err);
   336 	}
   337 }
   338 
   339 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
   340 {
   341 	if (m_status != Plugin_Created)
   342 	{
   343 		return false;
   344 	}
   345 
   346 	if (!error)
   347 	{
   348 		error = m_errormsg;
   349 		maxlength = sizeof(m_errormsg);
   350 	}
   351 
   352 	m_status = Plugin_Loaded;
   353 
   354 	int err;
   355 	cell_t result;
   356 	IPluginFunction *pFunction = GetFunctionByName("AskPluginLoad");
   357 
   358 	if (!pFunction)
   359 	{
   360 		return true;
   361 	}
   362 
   363 	pFunction->PushCell(0);		//:TODO: handle to ourself
   364 	pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
   365 	pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
   366 	pFunction->PushCell(maxlength);
   367 	if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
   368 	{
   369 		/* :TODO: debugging system */
   370 		snprintf(error, maxlength, "Plugin load returned run time error %d", err);
   371 		m_status = Plugin_Error;
   372 		return false;
   373 	}
   374 
   375 	if (!result)
   376 	{
   377 		m_status = Plugin_Error;
   378 		return false;
   379 	}
   380 
   381 	return true;
   382 }
   383 
   384 const sp_plugin_t *CPlugin::GetPluginStructure() const
   385 {
   386 	return m_plugin;
   387 }
   388 
   389 IPluginContext *CPlugin::GetBaseContext() const
   390 {
   391 	return m_ctx.base;
   392 }
   393 
   394 sp_context_t *CPlugin::GetContext() const
   395 {
   396 	return m_ctx.ctx;
   397 }
   398 
   399 const char *CPlugin::GetFilename() const
   400 {
   401 	return m_filename;
   402 }
   403 
   404 PluginType CPlugin::GetType() const
   405 {
   406 	return m_type;
   407 }
   408 
   409 const sm_plugininfo_t *CPlugin::GetPublicInfo() const
   410 {
   411 	return &m_info;
   412 }
   413 
   414 unsigned int CPlugin::GetSerial() const
   415 {
   416 	return m_serial;
   417 }
   418 
   419 PluginStatus CPlugin::GetStatus() const
   420 {
   421 	return m_status;
   422 }
   423 
   424 bool CPlugin::IsDebugging() const
   425 {
   426 	if (!m_ctx.ctx)
   427 	{
   428 		return false;
   429 	}
   430 
   431 	return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
   432 }
   433 
   434 bool CPlugin::SetPauseState(bool paused)
   435 {
   436 	if (paused && GetStatus() != Plugin_Paused)
   437 	{
   438 		return false;
   439 	} else if (!paused && GetStatus() != Plugin_Running) {
   440 		return false;
   441 	}
   442 	
   443 	/* :TODO: execute some forwards or some crap */
   444 
   445 	return true;
   446 }
   447 
   448 IdentityToken_t CPlugin::GetIdentity()
   449 {
   450 	return 0;
   451 }
   452 
   453 /*******************
   454  * PLUGIN ITERATOR *
   455  *******************/
   456 
   457 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
   458 {
   459 	mylist = _mylist;
   460 	Reset();
   461 }
   462 
   463 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
   464 {
   465 	return (*current);
   466 }
   467 
   468 bool CPluginManager::CPluginIterator::MorePlugins()
   469 {
   470 	return (current != mylist->end());
   471 }
   472 
   473 void CPluginManager::CPluginIterator::NextPlugin()
   474 {
   475 	current++;
   476 }
   477 
   478 void CPluginManager::CPluginIterator::Release()
   479 {
   480 	g_PluginSys.ReleaseIterator(this);
   481 }
   482 
   483 CPluginManager::CPluginIterator::~CPluginIterator()
   484 {
   485 }
   486 
   487 void CPluginManager::CPluginIterator::Reset()
   488 {
   489 	current = mylist->begin();
   490 }
   491 
   492 /******************
   493  * PLUGIN MANAGER *
   494  ******************/
   495 
   496 CPluginManager::CPluginManager()
   497 {
   498 	m_LoadLookup = sm_trie_create();
   499 	m_AllPluginsLoaded = false;
   500 }
   501 
   502 CPluginManager::~CPluginManager()
   503 {
   504 	//:TODO: we need a good way to free what we're holding
   505 	sm_trie_destroy(m_LoadLookup);
   506 }
   507 
   508 
   509 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
   510 {
   511 	/* First read in the database of plugin settings */
   512 	SMCParseError err;
   513 	unsigned int line, col;
   514 	m_AllPluginsLoaded = false;
   515 	if ((err=g_TextParse.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
   516 	{
   517 		/* :TODO: log the error, don't bail out though */
   518 	}
   519 
   520 	LoadPluginsFromDir(basedir, NULL);
   521 }
   522 
   523 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
   524 {
   525 	char base_path[PLATFORM_MAX_PATH+1];
   526 
   527 	/* Form the current path to start reading from */
   528 	if (localpath == NULL)
   529 	{
   530 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
   531 	} else {
   532 		g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
   533 	}
   534 
   535 	IDirectory *dir = g_LibSys.OpenDirectory(base_path);
   536 
   537 	if (!dir)
   538 	{
   539 		//:TODO: write a logger and LOG THIS UP, BABY
   540 		//g_LibSys.GetPlatformError(error, err_max);
   541 		return;
   542 	}
   543 
   544 	while (dir->MoreFiles())
   545 	{
   546 		if (dir->IsEntryDirectory() 
   547 			&& (strcmp(dir->GetEntryName(), ".") != 0)
   548 			&& (strcmp(dir->GetEntryName(), "..") != 0)
   549 			&& (strcmp(dir->GetEntryName(), "disabled") != 0)
   550 			&& (strcmp(dir->GetEntryName(), "optional") != 0))
   551 		{
   552 			char new_local[PLATFORM_MAX_PATH+1];
   553 			if (localpath == NULL)
   554 			{
   555 				/* If no path yet, don't add a former slash */
   556 				snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
   557 			} else {
   558 				g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
   559 			}
   560 			LoadPluginsFromDir(basedir, new_local);
   561 		} else if (dir->IsEntryFile()) {
   562 			const char *name = dir->GetEntryName();
   563 			size_t len = strlen(name);
   564 			if (len >= 4
   565 				&& strcmp(&name[len-4], ".smx") == 0)
   566 			{
   567 				/* If the filename matches, load the plugin */
   568 				char plugin[PLATFORM_MAX_PATH+1];
   569 				if (localpath == NULL)
   570 				{
   571 					snprintf(plugin, sizeof(plugin), "%s", name);
   572 				} else {
   573 					g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
   574 				}
   575 				LoadAutoPlugin(plugin);
   576 			}
   577 		}
   578 		dir->NextEntry();
   579 	}
   580 	g_LibSys.CloseDirectory(dir);
   581 }
   582 
   583 //well i have discovered that gabe newell is very fat, so i wrote this comment now
   584 //:TODO: remove this function, create a better wrapper for LoadPlugin()/LoadAutoPlugin()
   585 void CPluginManager::LoadAutoPlugin(const char *file)
   586 {
   587 	/**
   588 	 * Does this plugin already exist?
   589 	 */
   590 	CPlugin *pPlugin;
   591 	if (sm_trie_retrieve(m_LoadLookup, file, (void **)&pPlugin))
   592 	{
   593 		/* First check the type */
   594 		PluginType type = pPlugin->GetType();
   595 		if (type == PluginType_Private
   596 			|| type == PluginType_Global)
   597 		{
   598 			return;
   599 		}
   600 		/* Check to see if we should try reloading it */
   601 		if (pPlugin->GetStatus() == Plugin_BadLoad
   602 			|| pPlugin->GetStatus() == Plugin_Error)
   603 		{
   604 			UnloadPlugin(pPlugin);
   605 		}
   606 	}
   607 
   608 	pPlugin = CPlugin::CreatePlugin(file, NULL, 0);
   609 
   610 	assert(pPlugin != NULL);
   611 
   612 	pPlugin->m_type = PluginType_MapUpdated;
   613 
   614 	ICompilation *co = NULL;
   615 
   616 	if (pPlugin->m_status == Plugin_Uncompiled)
   617 	{
   618 		co = pPlugin->StartMyCompile(g_pVM);
   619 	}
   620 
   621 	PluginSettings *pset;
   622 	unsigned int setcount = m_PluginInfo.GetSettingsNum();
   623 	for (unsigned int i=0; i<setcount; i++)
   624 	{
   625 		pset = m_PluginInfo.GetSettingsIfMatch(i, file);
   626 		if (!pset)
   627 		{
   628 			continue;
   629 		}
   630 		pPlugin->m_type = pset->type_val;
   631 		if (co)
   632 		{
   633 			for (unsigned int j=0; j<pset->opts_num; j++)
   634 			{
   635 				const char *key, *val;
   636 				m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
   637 				if (!key || !val)
   638 				{
   639 					continue;
   640 				}
   641 				if (!g_pVM->SetCompilationOption(co, key, val))
   642 				{
   643 					pPlugin->SetErrorState(Plugin_Error, "Unable to set option (key \"%s\") (value \"%s\")", key, val);
   644 					pPlugin->CancelMyCompile();
   645 					co = NULL;
   646 					break;
   647 				}
   648 			}
   649 		}
   650 	}
   651 
   652 	if (co)
   653 	{
   654 		pPlugin->FinishMyCompile(NULL, 0);
   655 		co = NULL;
   656 	}
   657 
   658 	/* We don't care about the return value */
   659 	if (pPlugin->GetStatus() == Plugin_Created)
   660 	{
   661 		AddCoreNativesToPlugin(pPlugin);
   662 		pPlugin->Call_AskPluginLoad(NULL, 0);
   663 	}
   664 
   665 	AddPlugin(pPlugin);
   666 }
   667 
   668 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
   669 {
   670 	/* See if this plugin is already loaded... reformat to get sep chars right */
   671 	char checkpath[PLATFORM_MAX_PATH+1];
   672 	g_LibSys.PathFormat(checkpath, sizeof(checkpath), "%s", path);
   673 
   674 	/**
   675 	 * In manually loading a plugin, any sort of load error causes a deletion.
   676 	 * This is because it's assumed manually loaded plugins will not be managed.
   677 	 * For managed plugins, we need the UI to report them properly.
   678 	 */
   679 
   680 	CPlugin *pPlugin;
   681 	if (sm_trie_retrieve(m_LoadLookup, checkpath, (void **)&pPlugin))
   682 	{
   683 		snprintf(error, err_max, "Plugin file is already loaded");
   684 		return NULL;
   685 	}
   686 
   687 	pPlugin = CPlugin::CreatePlugin(path, error, err_max);
   688 
   689 	if (!pPlugin)
   690 	{
   691 		return NULL;
   692 	}
   693 
   694 	ICompilation *co = pPlugin->StartMyCompile(g_pVM);
   695 	if (!co || (debug && !g_pVM->SetCompilationOption(co, "debug", "1")))
   696 	{
   697 		snprintf(error, err_max, "Unable to start%s compilation", debug ? " debug" : "");
   698 		pPlugin->CancelMyCompile();
   699 		delete pPlugin;
   700 		return NULL;
   701 	}
   702 
   703 	if (!pPlugin->FinishMyCompile(error, err_max))
   704 	{
   705 		delete pPlugin;
   706 		return NULL;
   707 	}
   708 
   709 	pPlugin->m_type = type;
   710 
   711 	AddCoreNativesToPlugin(pPlugin);
   712 
   713 	/* Finally, ask the plugin if it wants to be loaded */
   714 	if (!pPlugin->Call_AskPluginLoad(error, err_max))
   715 	{
   716 		delete pPlugin;
   717 		return NULL;
   718 	}
   719 
   720 	AddPlugin(pPlugin);
   721 
   722 	return pPlugin;
   723 }
   724 
   725 void CPluginManager::AddPlugin(CPlugin *pPlugin)
   726 {
   727 	m_plugins.push_back(pPlugin);
   728 	sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
   729 
   730 	List<IPluginsListener *>::iterator iter;
   731 	IPluginsListener *pListener;
   732 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   733 	{
   734 		pListener = (*iter);
   735 		pListener->OnPluginCreated(pPlugin);
   736 	}
   737 
   738 	/* If the second pass was already completed, we have to run the pass on this plugin */
   739 	if (m_AllPluginsLoaded && pPlugin->GetStatus() == Plugin_Loaded)
   740 	{
   741 		RunSecondPass(pPlugin);
   742 	}
   743 }
   744 
   745 void CPluginManager::LoadAll_SecondPass()
   746 {
   747 	List<CPlugin *>::iterator iter;
   748 	CPlugin *pPlugin;
   749 
   750 	for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
   751 	{
   752 		pPlugin = (*iter);
   753 		if (pPlugin->GetStatus() == Plugin_Loaded)
   754 		{
   755 			RunSecondPass(pPlugin);
   756 		}
   757 	}
   758 	m_AllPluginsLoaded = true;
   759 }
   760 
   761 void CPluginManager::RunSecondPass(CPlugin *pPlugin)
   762 {
   763 	/* Tell this plugin to finish initializing itself */
   764 	pPlugin->Call_OnPluginInit();
   765 
   766 	/* Finish by telling all listeners */
   767 	List<IPluginsListener *>::iterator iter;
   768 	IPluginsListener *pListener;
   769 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   770 	{
   771 		pListener = (*iter);
   772 		pListener->OnPluginLoaded(pPlugin);
   773 	}
   774 }
   775 
   776 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
   777 {
   778 	List<sp_nativeinfo_t *>::iterator iter;
   779 
   780 	for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
   781 	{
   782 		sp_nativeinfo_t *natives = (*iter);
   783 		IPluginContext *ctx = pPlugin->GetBaseContext();
   784 		unsigned int i=0;
   785 		/* Attempt to bind every native! */
   786 		while (natives[i].func != NULL)
   787 		{
   788 			ctx->BindNative(&natives[i++]);
   789 		}
   790 	}
   791 }
   792 
   793 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
   794 {
   795 	CPlugin *pPlugin = (CPlugin *)plugin;
   796 
   797 	/* :TODO: More */
   798 
   799 	List<IPluginsListener *>::iterator iter;
   800 	IPluginsListener *pListener;
   801 	for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
   802 	{
   803 		pListener = (*iter);
   804 		pListener->OnPluginDestroyed(pPlugin);
   805 	}
   806 
   807 	m_plugins.remove(pPlugin);
   808 	sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
   809 	
   810 	delete pPlugin;
   811 
   812 	return true;
   813 }
   814 
   815 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
   816 {
   817 	IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
   818 	return pl;
   819 }
   820 
   821 unsigned int CPluginManager::GetPluginCount()
   822 {
   823 	return m_plugins.size();
   824 }
   825 
   826 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
   827 {
   828 	m_listeners.push_back(listener);
   829 }
   830 
   831 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
   832 {
   833 	m_listeners.remove(listener);
   834 }
   835 
   836 IPluginIterator *CPluginManager::GetPluginIterator()
   837 {
   838 	if (m_iters.empty())
   839 	{
   840 		return new CPluginIterator(&m_plugins);
   841 	} else {
   842 		CPluginIterator *iter = m_iters.front();
   843 		m_iters.pop();
   844 		iter->Reset();
   845 		return iter;
   846 	}
   847 }
   848 
   849 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
   850 {
   851 	m_iters.push(iter);
   852 }
   853 
   854 void CPluginManager::ReleaseFunctionToPool(CFunction *func)
   855 {
   856 	if (!func)
   857 	{
   858 		return;
   859 	}
   860 	func->Cancel();
   861 	m_funcpool.push(func);
   862 }
   863 
   864 CFunction *CPluginManager::GetFunctionFromPool(funcid_t f, CPlugin *plugin)
   865 {
   866 	if (m_funcpool.empty())
   867 	{
   868 		return new CFunction(f, plugin);
   869 	} else {
   870 		CFunction *func = m_funcpool.front();
   871 		m_funcpool.pop();
   872 		func->Set(f, plugin);
   873 		return func;
   874 	}
   875 }
   876 
   877 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
   878 {
   879 	/* As an optimization, we do not call strlen, but compute the length in the first pass */
   880 	size_t alias_len = 0;
   881 	size_t local_len = 0;
   882 
   883 	const char *ptr = alias;
   884 	unsigned int alias_explicit_paths = 0;
   885 	unsigned int alias_path_end = 0;
   886 	while (*ptr != '\0')
   887 	{
   888 		if (*ptr == '\\' || *ptr == '/')
   889 		{
   890 			alias_explicit_paths++;
   891 			alias_path_end = alias_len;
   892 		}
   893 		alias_len++;
   894 		ptr++;
   895 	}
   896 
   897 	if (alias_explicit_paths && alias_path_end == alias_len - 1)
   898 	{
   899 		/* Trailing slash is totally invalid here */
   900 		return false;
   901 	}
   902 
   903 	ptr = localpath;
   904 	unsigned int local_explicit_paths = 0;
   905 	unsigned int local_path_end = 0;
   906 	while (*ptr != '\0')
   907 	{
   908 		if (*ptr == '\\' || *ptr == '/')
   909 		{
   910 			local_explicit_paths++;
   911 			local_path_end = local_len;
   912 		}
   913 		local_len++;
   914 		ptr++;
   915 	}
   916 
   917 	/* If the alias has more explicit paths than the real path,
   918 	 * no match will be possible.
   919 	 */
   920 	if (alias_explicit_paths > local_explicit_paths)
   921 	{
   922 		return false;
   923 	}
   924 
   925 	if (alias_explicit_paths)
   926 	{
   927 		/* We need to find if the paths match now.  For example, these should all match:
   928 		 * csdm     csdm
   929 		 * csdm     optional/csdm
   930 		 * csdm/ban optional/crab/csdm/ban
   931 		*/
   932 		const char *aliasptr = alias;
   933 		const char *localptr = localpath;
   934 		bool match = true;
   935 		do
   936 		{
   937 			if (*aliasptr != *localptr)
   938 			{
   939 				/* We have to knock one path off */
   940 				local_explicit_paths--;
   941 				if (alias_explicit_paths > local_explicit_paths)
   942 				{
   943 					/* Skip out if we're gonna have an impossible match */
   944 					return false;
   945 				}
   946 				/* Eat up localptr tokens until we get a result */
   947 				while (((localptr - localpath) < (int)local_path_end)
   948 					&& *localptr != '/'
   949 					&& *localptr != '\\')
   950 				{
   951 					localptr++;
   952 				}
   953 				/* Check if we hit the end of our searchable area.
   954 				 * This probably isn't possible because of the path 
   955 				 * count check, but it's a good idea anyway.
   956 				 */
   957 				if ((localptr - localpath) >= (int)local_path_end)
   958 				{
   959 					return false;
   960 				} else {
   961 					/* Consume the slash token */
   962 					localptr++;
   963 				}
   964 				/* Reset the alias pointer so we can continue consuming */
   965 				aliasptr = alias;
   966 				match = false;
   967 				continue;
   968 			}
   969 			/* Note:
   970 			 * This is safe because if localptr terminates early, aliasptr will too
   971 			 */
   972 			do
   973 			{
   974 				/* We should never reach the end of the string because of this check. */
   975 				bool aliasend = (aliasptr - alias) > (int)alias_path_end;
   976 				bool localend = (localptr - localpath) > (int)local_path_end;
   977 				if (aliasend || localend)
   978 				{
   979 					if (aliasend && localend)
   980 					{
   981 						/* we matched, and we can break out now */
   982 						match = true;
   983 						break;
   984 					}
   985 					/* Otherwise, we've hit the end somehow and rest won't match up.  Break out. */
   986 					match = false;
   987 					break;
   988 				}
   989 
   990 				/* If we got here, it's safe to compare the next two tokens */
   991 				if (*localptr != *aliasptr)
   992 				{
   993 					match = false;
   994 					break;
   995 				}
   996 				localptr++;
   997 				aliasptr++;
   998 			} while (true);
   999 		} while (!match);
  1000 	}
  1001 
  1002 	/* If we got here, it's time to compare filenames */
  1003 	const char *aliasptr = alias;
  1004 	const char *localptr = localpath;
  1005 
  1006 	if (alias_explicit_paths)
  1007 	{
  1008 		aliasptr = &alias[alias_path_end + 1];
  1009 	}
  1010 
  1011 	if (local_explicit_paths)
  1012 	{
  1013 		localptr = &localpath[local_path_end + 1];
  1014 	}
  1015 
  1016 	while (true)
  1017 	{
  1018 		if (*aliasptr == '*')
  1019 		{
  1020 			/* First, see if this is the last character */
  1021 			if (aliasptr - alias == alias_len - 1)
  1022 			{
  1023 				/* If so, there's no need to match anything else */
  1024 				return true;
  1025 			}
  1026 			/* Otherwise, we need to search for an appropriate matching sequence in local.
  1027 			 * Note that we only need to search up to the next asterisk.
  1028 			 */
  1029 			aliasptr++;
  1030 			bool match = true;
  1031 			const char *local_orig = localptr;
  1032 			do
  1033 			{
  1034 				match = true;
  1035 				while (*aliasptr != '\0' && *aliasptr != '*')
  1036 				{
  1037 					/* Since aliasptr is never '\0', localptr hitting the end will fail */
  1038 					if (*aliasptr != *localptr)
  1039 					{
  1040 						match = false;
  1041 						break;
  1042 					}
  1043 					aliasptr++;
  1044 					localptr++;
  1045 				}
  1046 				if (!match)
  1047 				{
  1048 					/* If we didn't get a match, we need to advance the search stream.
  1049 					 * This will let us skip tokens while still searching for another match.
  1050 					 */
  1051 					localptr = ++local_orig;
  1052 					/* Make sure we don't go out of bounds */
  1053 					if (*localptr == '\0')
  1054 					{
  1055 						break;
  1056 					}
  1057 				}
  1058 			} while (!match);
  1059 
  1060 			if (!match)
  1061 			{
  1062 				return false;
  1063 			} else {
  1064 				/* If we got a match, move on to the next token */
  1065 				continue;
  1066 			}
  1067 		} else if (*aliasptr == '\0') {
  1068 			if (*localptr == '\0'
  1069 				||
  1070 				strcmp(localptr, ".smx") == 0)
  1071 			{
  1072 				return true;
  1073 			} else {
  1074 				return false;
  1075 			}
  1076 		} else if (*aliasptr != *localptr) {
  1077 			return false;
  1078 		}
  1079 		aliasptr++;
  1080 		localptr++;
  1081 	}
  1082 
  1083 	return true;
  1084 }
  1085 
  1086 bool CPluginManager::IsLateLoadTime()
  1087 {
  1088 	return (m_AllPluginsLoaded || g_SourceMod.IsLateLoadInMap());
  1089 }
  1090 
  1091 void CPluginManager::OnSourceModAllInitialized()
  1092 {
  1093 	HandleSecurity sec;
  1094 
  1095 	sec.owner = 1;	/* :TODO: implement ShareSys */
  1096 	sec.all.canCreate = false;
  1097 	sec.all.canDelete = false;
  1098 	sec.all.canInherit = false;
  1099 	
  1100 	g_PluginType = g_HandleSys.CreateTypeEx("IPlugin", this, 0, &sec);
  1101 }
  1102 
  1103 void CPluginManager::OnSourceModShutdown()
  1104 {
  1105 	g_HandleSys.RemoveType(g_PluginType, 1);
  1106 }
  1107 
  1108 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
  1109 {
  1110 	/* We don't care about the internal object, actually */
  1111 }