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