core/sourcemod.cpp
author Scott Ehlert <ds@alliedmods.net>
Sat Apr 21 17:03:28 2007 +0000 (2007-04-21)
changeset 702 ff520b71249c
parent 696 d5cc8d0fa899
child 718 8a5d1da7dd18
permissions -rw-r--r--
Wow, how did I forget this - exported IMemoryUtils interface
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <stdio.h>
    16 #include "sourcemod.h"
    17 #include "sourcemm_api.h"
    18 #include "systems/LibrarySys.h"
    19 #include "vm/sp_vm_engine.h"
    20 #include <sh_string.h>
    21 #include "PluginSys.h"
    22 #include "ShareSys.h"
    23 #include "CoreConfig.h"
    24 #include "Logger.h"
    25 #include "ExtensionSys.h"
    26 #include "AdminCache.h"
    27 #include "sm_stringutil.h"
    28 #include "PlayerManager.h"
    29 #include "Translator.h"
    30 #include "ForwardSys.h"
    31 #include "TimerSys.h"
    32 
    33 SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
    34 SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false);
    35 SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool);
    36 
    37 SourcePawnEngine g_SourcePawn;
    38 SourceModBase g_SourceMod;
    39 
    40 ILibrary *g_pJIT = NULL;
    41 SourceHook::String g_BaseDir;
    42 ISourcePawnEngine *g_pSourcePawn = &g_SourcePawn;
    43 IVirtualMachine *g_pVM;
    44 IdentityToken_t *g_pCoreIdent = NULL;
    45 float g_LastTime = 0.0f;
    46 float g_LastAuthCheck = 0.0f;
    47 IForward *g_pOnGameFrame = NULL;
    48 bool g_Loaded = false;
    49 
    50 typedef int (*GIVEENGINEPOINTER)(ISourcePawnEngine *);
    51 typedef unsigned int (*GETEXPORTCOUNT)();
    52 typedef IVirtualMachine *(*GETEXPORT)(unsigned int);
    53 typedef void (*NOTIFYSHUTDOWN)();
    54 
    55 void ShutdownJIT()
    56 {
    57 	NOTIFYSHUTDOWN notify = (NOTIFYSHUTDOWN)g_pJIT->GetSymbolAddress("NotifyShutdown");
    58 	if (notify)
    59 	{
    60 		notify();
    61 	}
    62 
    63 	g_pJIT->CloseLibrary();
    64 }
    65 
    66 SourceModBase::SourceModBase()
    67 {
    68 	m_IsMapLoading = false;
    69 	m_ExecPluginReload = false;
    70 	m_GotBasePath = false;
    71 }
    72 
    73 ConfigResult SourceModBase::OnSourceModConfigChanged(const char *key, 
    74 									  const char *value, 
    75 									  ConfigSource source, 
    76 									  char *error, 
    77 									  size_t maxlength)
    78 {
    79 	if (strcasecmp(key, "BasePath") == 0)
    80 	{
    81 		if (source == ConfigSource_Console)
    82 		{
    83 			UTIL_Format(error, maxlength, "Cannot be set at runtime");
    84 			return ConfigResult_Reject;
    85 		}
    86 
    87 		if (!m_GotBasePath)
    88 		{
    89 			g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/%s", g_BaseDir.c_str(), value);
    90 			g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), value);
    91 
    92 			m_GotBasePath = true;
    93 		}
    94 
    95 		return ConfigResult_Accept;
    96 	}
    97 
    98 	return ConfigResult_Ignore;
    99 }
   100 
   101 bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late)
   102 {
   103 	const char *gamepath = g_SMAPI->GetBaseDir();
   104 
   105 	/* Store full path to game */
   106 	g_BaseDir.assign(gamepath);
   107 
   108 	/* Store name of game directory by itself */
   109 	size_t len = strlen(gamepath);
   110 	for (size_t i = len - 1; i >= 0; i--)
   111 	{
   112 		if (gamepath[i] == PLATFORM_SEP_CHAR)
   113 		{
   114 			strncopy(m_ModDir, &gamepath[++i], sizeof(m_ModDir));
   115 			break;
   116 		}
   117 	}
   118 
   119 	/* Initialize CoreConfig so we can get SourceMod base path properly - this basically parses core.cfg */
   120 	g_CoreConfig.Initialize();
   121 
   122 	/* This shouldn't happen, but can't hurt to be safe */
   123 	if (!g_LibSys.PathExists(m_SMBaseDir) || !m_GotBasePath)
   124 	{
   125 		g_LibSys.PathFormat(m_SMBaseDir, sizeof(m_SMBaseDir), "%s/addons/sourcemod", g_BaseDir.c_str());
   126 		g_LibSys.PathFormat(m_SMRelDir, sizeof(m_SMRelDir), "addons/sourcemod");
   127 		m_GotBasePath = true;
   128 	}
   129 
   130 	/* Attempt to load the JIT! */
   131 	char file[PLATFORM_MAX_PATH];
   132 	char myerror[255];
   133 	g_SMAPI->PathFormat(file, sizeof(file), "%s/bin/sourcepawn.jit.x86.%s",
   134 		GetSourceModPath(),
   135 		PLATFORM_LIB_EXT
   136 		);
   137 
   138 	g_pJIT = g_LibSys.OpenLibrary(file, myerror, sizeof(myerror));
   139 	if (!g_pJIT)
   140 	{
   141 		if (error && err_max)
   142 		{
   143 			snprintf(error, err_max, "%s (failed to load bin/sourcepawn.jit.x86.%s)", 
   144 				myerror,
   145 				PLATFORM_LIB_EXT);
   146 		}
   147 		return false;
   148 	}
   149 
   150 	int err;
   151 	GIVEENGINEPOINTER jit_init = (GIVEENGINEPOINTER)g_pJIT->GetSymbolAddress("GiveEnginePointer");
   152 	if (!jit_init)
   153 	{
   154 		ShutdownJIT();
   155 		if (error && err_max)
   156 		{
   157 			snprintf(error, err_max, "Failed to find GiveEnginePointer in JIT!");
   158 		}
   159 		return false;
   160 	}
   161 
   162 	if ((err=jit_init(g_pSourcePawn)) != 0)
   163 	{
   164 		ShutdownJIT();
   165 		if (error && err_max)
   166 		{
   167 			snprintf(error, err_max, "GiveEnginePointer returned %d in the JIT", err);
   168 		}
   169 		return false;
   170 	}
   171 
   172 	GETEXPORTCOUNT jit_getnum = (GETEXPORTCOUNT)g_pJIT->GetSymbolAddress("GetExportCount");
   173 	GETEXPORT jit_get = (GETEXPORT)g_pJIT->GetSymbolAddress("GetExport");
   174 	if (!jit_get)
   175 	{
   176 		ShutdownJIT();
   177 		if (error && err_max)
   178 		{
   179 			snprintf(error, err_max, "JIT is missing a necessary export!");
   180 		}
   181 		return false;
   182 	}
   183 
   184 	unsigned int num = jit_getnum();
   185 	if (!num || ((g_pVM=jit_get(0)) == NULL))
   186 	{
   187 		ShutdownJIT();
   188 		if (error && err_max)
   189 		{
   190 			snprintf(error, err_max, "JIT did not export any virtual machines!");
   191 		}
   192 		return false;
   193 	}
   194 
   195 	unsigned int api = g_pVM->GetAPIVersion();
   196 	if (api != SOURCEPAWN_VM_API_VERSION)
   197 	{
   198 		ShutdownJIT();
   199 		if (error && err_max)
   200 		{
   201 			snprintf(error, err_max, "JIT is not a compatible version");
   202 		}
   203 		return false;
   204 	}
   205 
   206 	/* Hook this now so we can detect startup without calling StartSourceMod() */
   207 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
   208 
   209 	/* Only load if we're not late */
   210 	if (!late)
   211 	{
   212 		StartSourceMod(false);
   213 	}
   214 
   215 	return true;
   216 }
   217 
   218 void SourceModBase::StartSourceMod(bool late)
   219 {
   220 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false);
   221 	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false);
   222 
   223 	enginePatch = SH_GET_CALLCLASS(engine);
   224 	gamedllPatch = SH_GET_CALLCLASS(gamedll);
   225 
   226 	/* Notify! */
   227 	SMGlobalClass *pBase = SMGlobalClass::head;
   228 	while (pBase)
   229 	{
   230 		pBase->OnSourceModStartup(false);
   231 		pBase = pBase->m_pGlobalClassNext;
   232 	}
   233 
   234 	/* Make the global core identity */
   235 	g_pCoreIdent = g_ShareSys.CreateCoreIdentity();
   236 
   237 	/* Notify! */
   238 	pBase = SMGlobalClass::head;
   239 	while (pBase)
   240 	{
   241 		pBase->OnSourceModAllInitialized();
   242 		pBase = pBase->m_pGlobalClassNext;
   243 	}
   244 
   245 	/* Add us now... */
   246 	g_ShareSys.AddInterface(NULL, this);
   247 
   248 	/* We're loaded! */
   249 	g_Loaded = true;
   250 }
   251 
   252 bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
   253 {
   254 	/* If we're not loaded... */
   255 	if (!g_Loaded)
   256 	{
   257 		/* Do all global initialization now */
   258 		StartSourceMod(true);
   259 	}
   260 
   261 	m_IsMapLoading = true;
   262 	m_ExecPluginReload = true;
   263 	g_LastTime = 0.0f;
   264 	g_LastAuthCheck = 0.0f;
   265 
   266 	/* Notify! */
   267 	SMGlobalClass *pBase = SMGlobalClass::head;
   268 	while (pBase)
   269 	{
   270 		pBase->OnSourceModLevelChange(pMapName);
   271 		pBase = pBase->m_pGlobalClassNext;
   272 	}
   273 
   274 	DoGlobalPluginLoads();
   275 
   276 	m_IsMapLoading = false;
   277 
   278 	/* Notify! */
   279 	pBase = SMGlobalClass::head;
   280 	while (pBase)
   281 	{
   282 		pBase->OnSourceModPluginsLoaded();
   283 		pBase = pBase->m_pGlobalClassNext;
   284 	}
   285 
   286 	if (!g_pOnGameFrame)
   287 	{
   288 		g_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
   289 	}
   290 
   291 	RETURN_META_VALUE(MRES_IGNORED, true);
   292 }
   293 
   294 void SourceModBase::GameFrame(bool simulating)
   295 {
   296 	/**
   297 	 * Note: This is all hardcoded rather than delegated to save
   298 	 * precious CPU cycles.
   299 	 */
   300 	float curtime = gpGlobals->curtime;
   301 	if (curtime - g_LastTime >= 0.1f)
   302 	{
   303 		if (m_CheckingAuth
   304 			&& (gpGlobals->curtime - g_LastAuthCheck > 0.7f))
   305 		{
   306 			g_LastAuthCheck = gpGlobals->curtime;
   307 			g_Players.RunAuthChecks();
   308 		}
   309 
   310 		g_Timers.RunFrame();
   311 		g_LastTime = curtime;
   312 	}
   313 
   314 	if (g_pOnGameFrame && g_pOnGameFrame->GetFunctionCount())
   315 	{
   316 		g_pOnGameFrame->Execute(NULL);
   317 	}
   318 }
   319 
   320 void SourceModBase::LevelShutdown()
   321 {
   322 	if (m_ExecPluginReload)
   323 	{
   324 		g_PluginSys.ReloadOrUnloadPlugins();
   325 		m_ExecPluginReload = false;
   326 	}
   327 }
   328 
   329 bool SourceModBase::IsMapLoading() const
   330 {
   331 	return m_IsMapLoading;
   332 }
   333 
   334 void SourceModBase::DoGlobalPluginLoads()
   335 {
   336 	char config_path[PLATFORM_MAX_PATH];
   337 	char plugins_path[PLATFORM_MAX_PATH];
   338 
   339 	BuildPath(Path_SM, config_path, 
   340 		sizeof(config_path),
   341 		"configs/plugin_settings.cfg");
   342 	BuildPath(Path_SM, plugins_path,
   343 		sizeof(plugins_path),
   344 		"plugins");
   345 
   346 	/* Load any auto extensions */
   347 	g_Extensions.TryAutoload();
   348 
   349 	/* Run the first pass */
   350 	g_PluginSys.LoadAll_FirstPass(config_path, plugins_path);
   351 
   352 	/* Mark any extensions as loaded */
   353 	g_Extensions.MarkAllLoaded();
   354 
   355 	/* No modules yet, it's safe to call this from here */
   356 	g_PluginSys.LoadAll_SecondPass();
   357 
   358 	/* Re-mark any extensions as loaded */
   359 	g_Extensions.MarkAllLoaded();
   360 }
   361 
   362 size_t SourceModBase::BuildPath(PathType type, char *buffer, size_t maxlength, char *format, ...)
   363 {
   364 	char _buffer[PLATFORM_MAX_PATH];
   365 	va_list ap;
   366 
   367 	va_start(ap, format);
   368 	vsnprintf(_buffer, PLATFORM_MAX_PATH, format, ap);
   369 	va_end(ap);
   370 
   371 	const char *base = NULL;
   372 	if (type == Path_Game)
   373 	{
   374 		base = GetModPath();
   375 	} else if (type == Path_SM) {
   376 		base = GetSourceModPath();
   377 	} else if (type == Path_SM_Rel) {
   378 		base = m_SMRelDir;
   379 	}
   380 
   381 	if (base)
   382 	{
   383 		return g_LibSys.PathFormat(buffer, maxlength, "%s/%s", base, _buffer);
   384 	} else {
   385 		return g_LibSys.PathFormat(buffer, maxlength, "%s", _buffer);
   386 	}
   387 }
   388 
   389 void SourceModBase::CloseSourceMod()
   390 {
   391 	SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false);
   392 
   393 	if (g_Loaded)
   394 	{
   395 		if (g_pOnGameFrame)
   396 		{
   397 			g_Forwards.ReleaseForward(g_pOnGameFrame);
   398 		}
   399 	
   400 		/* Notify! */
   401 		SMGlobalClass *pBase = SMGlobalClass::head;
   402 		while (pBase)
   403 		{
   404 			pBase->OnSourceModShutdown();
   405 			pBase = pBase->m_pGlobalClassNext;
   406 		}
   407 	
   408 		/* Delete all data packs */
   409 		CStack<CDataPack *>::iterator iter;
   410 		CDataPack *pd;
   411 		for (iter=m_freepacks.begin(); iter!=m_freepacks.end(); iter++)
   412 		{
   413 			pd = (*iter);
   414 			delete pd;
   415 		}
   416 		m_freepacks.popall();
   417 	
   418 		/* Notify! */
   419 		pBase = SMGlobalClass::head;
   420 		while (pBase)
   421 		{
   422 			pBase->OnSourceModAllShutdown();
   423 			pBase = pBase->m_pGlobalClassNext;
   424 		}
   425 	
   426 		if (enginePatch)
   427 		{
   428 			SH_RELEASE_CALLCLASS(enginePatch);
   429 			enginePatch = NULL;
   430 		}
   431 	
   432 		if (gamedllPatch)
   433 		{
   434 			SH_RELEASE_CALLCLASS(gamedllPatch);
   435 			gamedllPatch = NULL;
   436 		}
   437 
   438 		SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false);
   439 		SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, gamedll, this, &SourceModBase::GameFrame, false);
   440 	}
   441 
   442 	/* Rest In Peace */
   443 	ShutdownJIT();
   444 }
   445 
   446 void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
   447 {
   448 	IExtensionInterface *pAPI = pExt->GetAPI();
   449 	const char *tag = pAPI->GetExtensionTag();
   450 	char buffer[2048];
   451 	va_list ap;
   452 
   453 	va_start(ap, format);
   454 	vsnprintf(buffer, sizeof(buffer), format, ap);
   455 	va_end(ap);
   456 
   457 	if (tag)
   458 	{
   459 		g_Logger.LogMessage("[%s] %s", tag, buffer);
   460 	} else {
   461 		g_Logger.LogMessage("%s", buffer);
   462 	}
   463 }
   464 
   465 void SourceModBase::LogError(IExtension *pExt, const char *format, ...)
   466 {
   467 	IExtensionInterface *pAPI = pExt->GetAPI();
   468 	const char *tag = pAPI->GetExtensionTag();
   469 	char buffer[2048];
   470 	va_list ap;
   471 
   472 	va_start(ap, format);
   473 	vsnprintf(buffer, sizeof(buffer), format, ap);
   474 	va_end(ap);
   475 
   476 	if (tag)
   477 	{
   478 		g_Logger.LogError("[%s] %s", tag, buffer);
   479 	} else {
   480 		g_Logger.LogError("%s", buffer);
   481 	}
   482 }
   483 
   484 size_t SourceModBase::FormatString(char *buffer, size_t maxlength, IPluginContext *pContext, const cell_t *params, unsigned int param)
   485 {
   486 	char *fmt;
   487 
   488 	pContext->LocalToString(params[param], &fmt);
   489 
   490 	int lparam = ++param;
   491 
   492 	return atcprintf(buffer, maxlength, fmt, pContext, params, &lparam);
   493 }
   494 
   495 const char *SourceModBase::GetSourceModPath() const
   496 {
   497 	return m_SMBaseDir;
   498 }
   499 
   500 const char *SourceModBase::GetModPath() const
   501 {
   502 	return g_BaseDir.c_str();
   503 }
   504 
   505 void SourceModBase::SetGlobalTarget(unsigned int index)
   506 {
   507 	m_target = index;
   508 }
   509 
   510 void SourceModBase::SetAuthChecking(bool set)
   511 {
   512 	m_CheckingAuth = set;
   513 }
   514 
   515 unsigned int SourceModBase::GetGlobalTarget() const
   516 {
   517 	return m_target;
   518 }
   519 
   520 IDataPack *SourceModBase::CreateDataPack()
   521 {
   522 	CDataPack *pack;
   523 	if (m_freepacks.empty())
   524 	{
   525 		pack = new CDataPack;
   526 	} else {
   527 		pack = m_freepacks.front();
   528 		m_freepacks.pop();
   529 		pack->Initialize();
   530 	}
   531 	return pack;
   532 }
   533 
   534 void SourceModBase::FreeDataPack(IDataPack *pack)
   535 {
   536 	m_freepacks.push(static_cast<CDataPack *>(pack));
   537 }
   538 
   539 Handle_t SourceModBase::GetDataPackHandleType(bool readonly)
   540 {
   541 	//:TODO:
   542 	return 0;
   543 }
   544 
   545 const char *SourceModBase::GetModFolderName() const
   546 {
   547 	return m_ModDir;
   548 }
   549 
   550 SMGlobalClass *SMGlobalClass::head = NULL;
   551 
   552 SMGlobalClass::SMGlobalClass()
   553 {
   554 	m_pGlobalClassNext = SMGlobalClass::head;
   555 	SMGlobalClass::head = this;
   556 }