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