core/sourcemod.cpp
author David Anderson <dvander@alliedmods.net>
Mon May 26 07:51:11 2008 +0000 (2008-05-26)
changeset 2086 debb2ab2c782
parent 1952 b7aa2b626807
child 2193 db7c6362f3f2
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
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <stdio.h>
    33 #include "sourcemod.h"
    34 #include "sourcemm_api.h"
    35 #include "systems/LibrarySys.h"
    36 #include "vm/sp_vm_engine.h"
    37 #include <sh_string.h>
    38 #include "PluginSys.h"
    39 #include "ShareSys.h"
    40 #include "CoreConfig.h"
    41 #include "Logger.h"
    42 #include "ExtensionSys.h"
    43 #include "AdminCache.h"
    44 #include "sm_stringutil.h"
    45 #include "PlayerManager.h"
    46 #include "Translator.h"
    47 #include "ForwardSys.h"
    48 #include "TimerSys.h"
    49 #include "GameConfigs.h"
    50 
    51 SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
    52 SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
    53 SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool);
    54 SH_DECL_HOOK1_void(IVEngineServer, ServerCommand, SH_NOATTRIB, false, const char *);
    55 
    56 SourcePawnEngine g_SourcePawn;
    57 SourceModBase g_SourceMod;
    58 
    59 ILibrary *g_pJIT = NULL;
    60 SourceHook::String g_BaseDir;
    61 ISourcePawnEngine *g_pSourcePawn = &g_SourcePawn;
    62 IVirtualMachine *g_pVM;
    63 IdentityToken_t *g_pCoreIdent = NULL;
    64 IForward *g_pOnMapEnd = NULL;
    65 bool g_Loaded = false;
    66 
    67 typedef int (*GIVEENGINEPOINTER)(ISourcePawnEngine *);
    68 typedef int (*GIVEENGINEPOINTER2)(ISourcePawnEngine *, unsigned int api_version);
    69 typedef unsigned int (*GETEXPORTCOUNT)();
    70 typedef IVirtualMachine *(*GETEXPORT)(unsigned int);
    71 typedef void (*NOTIFYSHUTDOWN)();
    72 
    73 void ShutdownJIT()
    74 {
    75 	NOTIFYSHUTDOWN notify = (NOTIFYSHUTDOWN)g_pJIT->GetSymbolAddress("NotifyShutdown");
    76 	if (notify)
    77 	{
    78 		notify();
    79 	}
    80 
    81 	g_pJIT->CloseLibrary();
    82 }
    83 
    84 SourceModBase::SourceModBase()
    85 {
    86 	m_IsMapLoading = false;
    87 	m_ExecPluginReload = false;
    88 	m_GotBasePath = false;
    89 }
    90 
    91 ConfigResult SourceModBase::OnSourceModConfigChanged(const char *key, 
    92 									  const char *value, 
    93 									  ConfigSource source, 
    94 									  char *error, 
    95 									  size_t maxlength)
    96 {
    97 	if (strcasecmp(key, "BasePath") == 0)
    98 	{
    99 		if (source == ConfigSource_Console)
   100 		{
   101 			UTIL_Format(error, maxlength, "Cannot be set at runtime");
   102 			return ConfigResult_Reject;
   103 		}
   104 
   105 		if (!m_GotBasePath)
   106 		{
   107 			g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/%s", g_BaseDir.c_str(), value);
   108 			g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), value);
   109 
   110 			m_GotBasePath = true;
   111 		}
   112 
   113 		return ConfigResult_Accept;
   114 	}
   115 
   116 	return ConfigResult_Ignore;
   117 }
   118 
   119 bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late)
   120 {
   121 	const char *gamepath = g_SMAPI->GetBaseDir();
   122 
   123 	/* Store full path to game */
   124 	g_BaseDir.assign(gamepath);
   125 
   126 	/* Store name of game directory by itself */
   127 	size_t len = strlen(gamepath);
   128 	for (size_t i = len - 1; i >= 0; i--)
   129 	{
   130 		if (gamepath[i] == PLATFORM_SEP_CHAR)
   131 		{
   132 			strncopy(m_ModDir, &gamepath[++i], sizeof(m_ModDir));
   133 			break;
   134 		}
   135 	}
   136 
   137 	/* Initialize CoreConfig so we can get SourceMod base path properly - this basically parses core.cfg */
   138 	g_CoreConfig.Initialize();
   139 
   140 	/* This shouldn't happen, but can't hurt to be safe */
   141 	if (!g_LibSys.PathExists(m_SMBaseDir) || !m_GotBasePath)
   142 	{
   143 		g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/addons/sourcemod", g_BaseDir.c_str());
   144 		g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), "addons/sourcemod");
   145 		m_GotBasePath = true;
   146 	}
   147 
   148 	/* Attempt to load the JIT! */
   149 	char file[PLATFORM_MAX_PATH];
   150 	char myerror[255];
   151 	g_SMAPI->PathFormat(file, sizeof(file), "%s/bin/sourcepawn.jit.x86.%s",
   152 		GetSourceModPath(),
   153 		PLATFORM_LIB_EXT
   154 		);
   155 
   156 	g_pJIT = g_LibSys.OpenLibrary(file, myerror, sizeof(myerror));
   157 	if (!g_pJIT)
   158 	{
   159 		if (error && maxlength)
   160 		{
   161 			snprintf(error, maxlength, "%s (failed to load bin/sourcepawn.jit.x86.%s)", 
   162 				myerror,
   163 				PLATFORM_LIB_EXT);
   164 		}
   165 		return false;
   166 	}
   167 
   168 	int err;
   169 	
   170 	GIVEENGINEPOINTER2 jit_init2 = (GIVEENGINEPOINTER2)g_pJIT->GetSymbolAddress("GiveEnginePointer2");
   171 	if (!jit_init2)
   172 	{
   173 		GIVEENGINEPOINTER jit_init = (GIVEENGINEPOINTER)g_pJIT->GetSymbolAddress("GiveEnginePointer");
   174 		if (!jit_init)
   175 		{
   176 			ShutdownJIT();
   177 			if (error && maxlength)
   178 			{
   179 				snprintf(error, maxlength, "Failed to find GiveEnginePointer in JIT!");
   180 			}
   181 			return false;
   182 		}
   183 
   184 		if ((err=jit_init(g_pSourcePawn)) != 0)
   185 		{
   186 			ShutdownJIT();
   187 			if (error && maxlength)
   188 			{
   189 				snprintf(error, maxlength, "GiveEnginePointer returned %d in the JIT", err);
   190 			}
   191 			return false;
   192 		}
   193 	}
   194 	else
   195 	{
   196 		/* On version bumps, we should check for older versions as well, if the new version fails.
   197 		 * We can then check the exports to see if any VM versions will be sufficient.
   198 		 */
   199 		if ((err=jit_init2(g_pSourcePawn, SOURCEPAWN_ENGINE_API_VERSION)) != SP_ERROR_NONE)
   200 		{
   201 			ShutdownJIT();
   202 			if (error && maxlength)
   203 			{
   204 				snprintf(error, maxlength, "JIT incompatible with SourceMod version");
   205 			}
   206 			return false;
   207 		}
   208 	}
   209 
   210 	GETEXPORTCOUNT jit_getnum = (GETEXPORTCOUNT)g_pJIT->GetSymbolAddress("GetExportCount");
   211 	GETEXPORT jit_get = (GETEXPORT)g_pJIT->GetSymbolAddress("GetExport");
   212 	if (!jit_get)
   213 	{
   214 		ShutdownJIT();
   215 		if (error && maxlength)
   216 		{
   217 			snprintf(error, maxlength, "JIT is missing a necessary export!");
   218 		}
   219 		return false;
   220 	}
   221 
   222 	unsigned int num = jit_getnum();
   223 	if (!num)
   224 	{
   225 		ShutdownJIT();
   226 		if (error && maxlength)
   227 		{
   228 			snprintf(error, maxlength, "JIT did not export any virtual machines!");
   229 		}
   230 		return false;
   231 	}
   232 
   233 	unsigned int api_version;
   234 	for (unsigned int i=0; i<num; i++)
   235 	{
   236 		 if ((g_pVM=jit_get(i)) == NULL)
   237 		 {
   238 			 if (error && maxlength)
   239 			 {
   240 				 snprintf(error, maxlength, "JIT did not export any virtual machines!");
   241 			 }
   242 			 continue;
   243 		 }
   244 		/* Refuse any API that we might not be able to deal with.
   245 		 * <s>Also refuse anything < 3 because we need fake natives.</s>
   246 		 * Also refuse anything < 7 because we need the new sp_native definition.
   247 		 */
   248 		 api_version = g_pVM->GetAPIVersion();
   249 		 if (api_version < 7 || api_version > SOURCEPAWN_VM_API_VERSION)
   250 		 {
   251 			 if (error && maxlength)
   252 			 {
   253 				 snprintf(error, maxlength, "JIT is not a compatible version");
   254 			 }
   255 			 g_pVM = NULL;
   256 			 continue;
   257 		 }
   258 		 break;
   259 	}
   260 
   261 	if (!g_pVM)
   262 	{
   263 		ShutdownJIT();
   264 		return false;
   265 	}
   266 
   267 	/* Hook this now so we can detect startup without calling StartSourceMod() */
   268 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
   269 
   270 	/* Only load if we're not late */
   271 	if (!late)
   272 	{
   273 		StartSourceMod(false);
   274 	}
   275 
   276 	return true;
   277 }
   278 
   279 void SourceModBase::StartSourceMod(bool late)
   280 {
   281 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false);
   282 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, &g_Timers, &TimerSystem::GameFrame, false);
   283 
   284 	enginePatch = SH_GET_CALLCLASS(engine);
   285 	gamedllPatch = SH_GET_CALLCLASS(gamedll);
   286 
   287 	g_ShareSys.Initialize();
   288 
   289 	/* Make the global core identity */
   290 	g_pCoreIdent = g_ShareSys.CreateCoreIdentity();
   291 
   292 	/* Notify! */
   293 	SMGlobalClass *pBase = SMGlobalClass::head;
   294 	while (pBase)
   295 	{
   296 		pBase->OnSourceModStartup(false);
   297 		pBase = pBase->m_pGlobalClassNext;
   298 	}
   299 
   300 	/* Notify! */
   301 	pBase = SMGlobalClass::head;
   302 	while (pBase)
   303 	{
   304 		pBase->OnSourceModAllInitialized();
   305 		pBase = pBase->m_pGlobalClassNext;
   306 	}
   307 
   308 	/* Notify! */
   309 	pBase = SMGlobalClass::head;
   310 	while (pBase)
   311 	{
   312 		pBase->OnSourceModAllInitialized_Post();
   313 		pBase = pBase->m_pGlobalClassNext;
   314 	}
   315 
   316 	/* Add us now... */
   317 	g_ShareSys.AddInterface(NULL, this);
   318 
   319 	/* We're loaded! */
   320 	g_Loaded = true;
   321 
   322 	/* Initialize VSP stuff */
   323 	if (vsp_interface != NULL)
   324 	{
   325 		g_SourceMod_Core.OnVSPListening(vsp_interface);
   326 	}
   327 }
   328 
   329 static bool g_LevelEndBarrier = false;
   330 bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
   331 {
   332 	/* If we're not loaded... */
   333 	if (!g_Loaded)
   334 	{
   335 		/* Do all global initialization now */
   336 		StartSourceMod(true);
   337 	}
   338 
   339 	m_IsMapLoading = true;
   340 	m_ExecPluginReload = true;
   341 
   342 	/* Notify! */
   343 	SMGlobalClass *pBase = SMGlobalClass::head;
   344 	while (pBase)
   345 	{
   346 		pBase->OnSourceModLevelChange(pMapName);
   347 		pBase = pBase->m_pGlobalClassNext;
   348 	}
   349 
   350 	DoGlobalPluginLoads();
   351 
   352 	m_IsMapLoading = false;
   353 
   354 	/* Notify! */
   355 	pBase = SMGlobalClass::head;
   356 	while (pBase)
   357 	{
   358 		pBase->OnSourceModPluginsLoaded();
   359 		pBase = pBase->m_pGlobalClassNext;
   360 	}
   361 
   362 	if (!g_pOnMapEnd)
   363 	{
   364 		g_pOnMapEnd = g_Forwards.CreateForward("OnMapEnd", ET_Ignore, 0, NULL);
   365 	}
   366 
   367 	g_LevelEndBarrier = true;
   368 
   369 	RETURN_META_VALUE(MRES_IGNORED, true);
   370 }
   371 
   372 void SourceModBase::LevelShutdown()
   373 {
   374 	if (g_LevelEndBarrier)
   375 	{
   376 		SMGlobalClass *next = SMGlobalClass::head;
   377 		while (next)
   378 		{
   379 			next->OnSourceModLevelEnd();
   380 			next = next->m_pGlobalClassNext;
   381 		}
   382 		
   383 		if (g_pOnMapEnd != NULL)
   384 		{
   385 			g_pOnMapEnd->Execute(NULL);
   386 		}
   387 
   388 		g_Timers.RemoveMapChangeTimers();
   389 
   390 		g_LevelEndBarrier = false;
   391 	}
   392 
   393 	g_OnMapStarted = false;
   394 
   395 	if (m_ExecPluginReload)
   396 	{
   397 		g_PluginSys.ReloadOrUnloadPlugins();
   398 		m_ExecPluginReload = false;
   399 	}
   400 }
   401 
   402 bool SourceModBase::IsMapLoading() const
   403 {
   404 	return m_IsMapLoading;
   405 }
   406 
   407 void SourceModBase::DoGlobalPluginLoads()
   408 {
   409 	char config_path[PLATFORM_MAX_PATH];
   410 	char plugins_path[PLATFORM_MAX_PATH];
   411 
   412 	BuildPath(Path_SM, config_path, 
   413 		sizeof(config_path),
   414 		"configs/plugin_settings.cfg");
   415 	BuildPath(Path_SM, plugins_path,
   416 		sizeof(plugins_path),
   417 		"plugins");
   418 
   419 	/* Load any auto extensions */
   420 	g_Extensions.TryAutoload();
   421 
   422 	/* Fire the extensions ready message */
   423 	g_SMAPI->MetaFactory(SOURCEMOD_NOTICE_EXTENSIONS, NULL, NULL);	
   424 
   425 	/* Load any game extension */
   426 	const char *game_ext;
   427 	if ((game_ext = g_pGameConf->GetKeyValue("GameExtension")) != NULL)
   428 	{
   429 		char path[PLATFORM_MAX_PATH];
   430 		UTIL_Format(path, sizeof(path), "%s.ext." PLATFORM_LIB_EXT, game_ext);
   431 		g_Extensions.LoadAutoExtension(path);
   432 	}
   433 
   434 	/* Run the first pass */
   435 	g_PluginSys.LoadAll_FirstPass(config_path, plugins_path);
   436 
   437 	/* Mark any extensions as loaded */
   438 	g_Extensions.MarkAllLoaded();
   439 
   440 	/* No modules yet, it's safe to call this from here */
   441 	g_PluginSys.LoadAll_SecondPass();
   442 
   443 	/* Re-mark any extensions as loaded */
   444 	g_Extensions.MarkAllLoaded();
   445 
   446 	/* Call OnAllPluginsLoaded */
   447 	g_PluginSys.AllPluginsLoaded();
   448 }
   449 
   450 size_t SourceModBase::BuildPath(PathType type, char *buffer, size_t maxlength, const char *format, ...)
   451 {
   452 	char _buffer[PLATFORM_MAX_PATH];
   453 	va_list ap;
   454 
   455 	va_start(ap, format);
   456 	vsnprintf(_buffer, PLATFORM_MAX_PATH, format, ap);
   457 	va_end(ap);
   458 
   459 	/* If we get a "file://" notation, strip off the file:// part so we're left 
   460 	 * with an absolute path.  Note that the absolute path gets returned, so 
   461 	 * usage with relative paths here is completely invalid.
   462 	 */
   463 	if (type != Path_SM_Rel && strncmp(_buffer, "file://", 7) == 0)
   464 	{
   465 		return g_LibSys.PathFormat(buffer, maxlength, "%s", &_buffer[7]);
   466 	}
   467 
   468 	const char *base = NULL;
   469 	if (type == Path_Game)
   470 	{
   471 		base = GetGamePath();
   472 	}
   473 	else if (type == Path_SM)
   474 	{
   475 		base = GetSourceModPath();
   476 	}
   477 	else if (type == Path_SM_Rel)
   478 	{
   479 		base = m_SMRelDir;
   480 	}
   481 
   482 	if (base)
   483 	{
   484 		return g_LibSys.PathFormat(buffer, maxlength, "%s/%s", base, _buffer);
   485 	}
   486 	else
   487 	{
   488 		return g_LibSys.PathFormat(buffer, maxlength, "%s", _buffer);
   489 	}
   490 }
   491 
   492 void SourceModBase::CloseSourceMod()
   493 {
   494 	/* Force a level end */
   495 	LevelShutdown();
   496 
   497 	/* Unload plugins */
   498 	g_PluginSys.Shutdown();
   499 
   500 	/* Unload extensions */
   501 	g_Extensions.Shutdown();
   502 
   503 	SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
   504 
   505 	if (g_Loaded)
   506 	{
   507 		if (g_pOnMapEnd)
   508 		{
   509 			g_Forwards.ReleaseForward(g_pOnMapEnd);
   510 		}
   511 	
   512 		/* Notify! */
   513 		SMGlobalClass *pBase = SMGlobalClass::head;
   514 		while (pBase)
   515 		{
   516 			pBase->OnSourceModShutdown();
   517 			pBase = pBase->m_pGlobalClassNext;
   518 		}
   519 	
   520 		/* Delete all data packs */
   521 		CStack<CDataPack *>::iterator iter;
   522 		CDataPack *pd;
   523 		for (iter=m_freepacks.begin(); iter!=m_freepacks.end(); iter++)
   524 		{
   525 			pd = (*iter);
   526 			delete pd;
   527 		}
   528 		m_freepacks.popall();
   529 	
   530 		/* Notify! */
   531 		pBase = SMGlobalClass::head;
   532 		while (pBase)
   533 		{
   534 			pBase->OnSourceModAllShutdown();
   535 			pBase = pBase->m_pGlobalClassNext;
   536 		}
   537 	
   538 		if (enginePatch)
   539 		{
   540 			SH_RELEASE_CALLCLASS(enginePatch);
   541 			enginePatch = NULL;
   542 		}
   543 	
   544 		if (gamedllPatch)
   545 		{
   546 			SH_RELEASE_CALLCLASS(gamedllPatch);
   547 			gamedllPatch = NULL;
   548 		}
   549 
   550 		SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false);
   551 		SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, &g_Timers, &TimerSystem::GameFrame, false);
   552 	}
   553 
   554 	/* Rest In Peace */
   555 	ShutdownJIT();
   556 }
   557 
   558 void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
   559 {
   560 	IExtensionInterface *pAPI = pExt->GetAPI();
   561 	const char *tag = pAPI->GetExtensionTag();
   562 	char buffer[2048];
   563 	va_list ap;
   564 
   565 	va_start(ap, format);
   566 	vsnprintf(buffer, sizeof(buffer), format, ap);
   567 	va_end(ap);
   568 
   569 	if (tag)
   570 	{
   571 		g_Logger.LogMessage("[%s] %s", tag, buffer);
   572 	} else {
   573 		g_Logger.LogMessage("%s", buffer);
   574 	}
   575 }
   576 
   577 void SourceModBase::LogError(IExtension *pExt, const char *format, ...)
   578 {
   579 	IExtensionInterface *pAPI = pExt->GetAPI();
   580 	const char *tag = pAPI->GetExtensionTag();
   581 	char buffer[2048];
   582 	va_list ap;
   583 
   584 	va_start(ap, format);
   585 	vsnprintf(buffer, sizeof(buffer), format, ap);
   586 	va_end(ap);
   587 
   588 	if (tag)
   589 	{
   590 		g_Logger.LogError("[%s] %s", tag, buffer);
   591 	} else {
   592 		g_Logger.LogError("%s", buffer);
   593 	}
   594 }
   595 
   596 size_t SourceModBase::FormatString(char *buffer, size_t maxlength, IPluginContext *pContext, const cell_t *params, unsigned int param)
   597 {
   598 	char *fmt;
   599 
   600 	pContext->LocalToString(params[param], &fmt);
   601 
   602 	int lparam = ++param;
   603 
   604 	return atcprintf(buffer, maxlength, fmt, pContext, params, &lparam);
   605 }
   606 
   607 const char *SourceModBase::GetSourceModPath() const
   608 {
   609 	return m_SMBaseDir;
   610 }
   611 
   612 const char *SourceModBase::GetGamePath() const
   613 {
   614 	return g_BaseDir.c_str();
   615 }
   616 
   617 unsigned int SourceModBase::SetGlobalTarget(unsigned int index)
   618 {
   619 	unsigned int old = m_target;
   620 	m_target = index;
   621 	return old;
   622 }
   623 
   624 unsigned int SourceModBase::GetGlobalTarget() const
   625 {
   626 	return m_target;
   627 }
   628 
   629 IDataPack *SourceModBase::CreateDataPack()
   630 {
   631 	CDataPack *pack;
   632 	if (m_freepacks.empty())
   633 	{
   634 		pack = new CDataPack;
   635 	} else {
   636 		pack = m_freepacks.front();
   637 		m_freepacks.pop();
   638 		pack->Initialize();
   639 	}
   640 	return pack;
   641 }
   642 
   643 void SourceModBase::FreeDataPack(IDataPack *pack)
   644 {
   645 	m_freepacks.push(static_cast<CDataPack *>(pack));
   646 }
   647 
   648 Handle_t SourceModBase::GetDataPackHandleType(bool readonly)
   649 {
   650 	//:TODO:
   651 	return 0;
   652 }
   653 
   654 const char *SourceModBase::GetGameFolderName() const
   655 {
   656 	return m_ModDir;
   657 }
   658 
   659 ISourcePawnEngine *SourceModBase::GetScriptingEngine()
   660 {
   661 	return g_pSourcePawn;
   662 }
   663 
   664 IVirtualMachine *SourceModBase::GetScriptingVM()
   665 {
   666 	return g_pVM;
   667 }
   668 
   669 void SourceModBase::AllPluginsLoaded()
   670 {
   671 	SMGlobalClass *base = SMGlobalClass::head;
   672 	while (base)
   673 	{
   674 		base->OnSourceModGameInitialized();
   675 		base = base->m_pGlobalClassNext;
   676 	}
   677 }
   678 
   679 time_t SourceModBase::GetAdjustedTime()
   680 {
   681 	return ::GetAdjustedTime();
   682 }
   683 
   684 void SourceModBase::AddGameFrameHook(GAME_FRAME_HOOK hook)
   685 {
   686 	m_frame_hooks.push_back(hook);
   687 }
   688 
   689 void SourceModBase::RemoveGameFrameHook(GAME_FRAME_HOOK hook)
   690 {
   691 	for (size_t i = 0; i < m_frame_hooks.size(); i++)
   692 	{
   693 		if (m_frame_hooks[i] == hook)
   694 		{
   695 			m_frame_hooks.erase(m_frame_hooks.iterAt(i));
   696 			return;
   697 		}
   698 	}
   699 }
   700 
   701 void SourceModBase::ProcessGameFrameHooks(bool simulating)
   702 {
   703 	if (m_frame_hooks.size() == 0)
   704 	{
   705 		return;
   706 	}
   707 
   708 	for (size_t i = 0; i < m_frame_hooks.size(); i++)
   709 	{
   710 		m_frame_hooks[i](simulating);
   711 	}
   712 }
   713 
   714 SMGlobalClass *SMGlobalClass::head = NULL;
   715 
   716 SMGlobalClass::SMGlobalClass()
   717 {
   718 	m_pGlobalClassNext = SMGlobalClass::head;
   719 	SMGlobalClass::head = this;
   720 }