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