extensions/sdktools/extension.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Fri Feb 07 22:11:31 2014 -0500 (2014-02-07)
changeset 4231 84b709f4103a
parent 4188 89782d5fc91b
permissions -rw-r--r--
Fix SDKHooks load issue and reliance on gEntList ptr on CS:GO and Dota 2 on Windows.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2010 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 <sourcemod_version.h>
    33 #include "extension.h"
    34 #include <compat_wrappers.h>
    35 #include "vcallbuilder.h"
    36 #include "vnatives.h"
    37 #include "vhelpers.h"
    38 #include "vglobals.h"
    39 #include "tempents.h"
    40 #include "vsound.h"
    41 #include "output.h"
    42 #include "hooks.h"
    43 #include "gamerulesnatives.h"
    44 #include <ISDKTools.h>
    45 #include "clientnatives.h"
    46 #include "teamnatives.h"
    47 /**
    48  * @file extension.cpp
    49  * @brief Implements SDK Tools extension code.
    50  */
    51 
    52 SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
    53 
    54 SDKTools g_SdkTools;		/**< Global singleton for extension's main interface */
    55 IServerGameEnts *gameents = NULL;
    56 IEngineTrace *enginetrace = NULL;
    57 IEngineSound *engsound = NULL;
    58 INetworkStringTableContainer *netstringtables = NULL;
    59 IServerPluginHelpers *pluginhelpers = NULL;
    60 IBinTools *g_pBinTools = NULL;
    61 IGameConfig *g_pGameConf = NULL;
    62 IGameHelpers *g_pGameHelpers = NULL;
    63 IServerGameClients *serverClients = NULL;
    64 IVoiceServer *voiceserver = NULL;
    65 IPlayerInfoManager *playerinfomngr = NULL;
    66 ICvar *icvar = NULL;
    67 IServer *iserver = NULL;
    68 CGlobalVars *gpGlobals;
    69 
    70 #if SOURCE_ENGINE >= SE_ORANGEBOX
    71 IServerTools *servertools = NULL;
    72 #endif
    73 
    74 SourceHook::CallClass<IVEngineServer> *enginePatch = NULL;
    75 SourceHook::CallClass<IEngineSound> *enginesoundPatch = NULL;
    76 HandleType_t g_CallHandle = 0;
    77 HandleType_t g_TraceHandle = 0;
    78 ISDKTools *g_pSDKTools;
    79 
    80 SMEXT_LINK(&g_SdkTools);
    81 
    82 extern sp_nativeinfo_t g_CallNatives[];
    83 extern sp_nativeinfo_t g_TENatives[];
    84 extern sp_nativeinfo_t g_TRNatives[];
    85 extern sp_nativeinfo_t g_StringTableNatives[];
    86 extern sp_nativeinfo_t g_VoiceNatives[];
    87 extern sp_nativeinfo_t g_EntInputNatives[];
    88 extern sp_nativeinfo_t g_TeamNatives[];
    89 extern sp_nativeinfo_t g_GameRulesNatives[];
    90 extern sp_nativeinfo_t g_ClientNatives[];
    91 
    92 static void InitSDKToolsAPI();
    93 
    94 bool SDKTools::SDK_OnLoad(char *error, size_t maxlength, bool late)
    95 {
    96 	HandleError err;
    97 
    98 	if (!gameconfs->LoadGameConfigFile("sdktools.games", &g_pGameConf, error, maxlength))
    99 	{
   100 		return false;
   101 	}
   102 
   103 	sharesys->AddDependency(myself, "bintools.ext", true, true);
   104 	sharesys->AddNatives(myself, g_CallNatives);
   105 	sharesys->AddNatives(myself, g_Natives);
   106 	sharesys->AddNatives(myself, g_TENatives);
   107 	sharesys->AddNatives(myself, g_SoundNatives);
   108 	sharesys->AddNatives(myself, g_TRNatives);
   109 	sharesys->AddNatives(myself, g_StringTableNatives);
   110 	sharesys->AddNatives(myself, g_VoiceNatives);
   111 	sharesys->AddNatives(myself, g_EntInputNatives);
   112 	sharesys->AddNatives(myself, g_TeamNatives);
   113 	sharesys->AddNatives(myself, g_EntOutputNatives);
   114 	sharesys->AddNatives(myself, g_GameRulesNatives);
   115 	sharesys->AddNatives(myself, g_ClientNatives);
   116 
   117 	SM_GET_IFACE(GAMEHELPERS, g_pGameHelpers);
   118 
   119 	playerhelpers->AddClientListener(&g_SdkTools);
   120 	g_CallHandle = handlesys->CreateType("ValveCall", this, 0, NULL, NULL, myself->GetIdentity(), &err);
   121 	if (g_CallHandle == 0)
   122 	{
   123 		snprintf(error, maxlength, "Could not create call handle type (err: %d)", err);	
   124 		return false;
   125 	}
   126 
   127 	TypeAccess TraceAccess;
   128 	handlesys->InitAccessDefaults(&TraceAccess, NULL);
   129 	TraceAccess.ident = myself->GetIdentity();
   130 	TraceAccess.access[HTypeAccess_Create] = true;
   131 	TraceAccess.access[HTypeAccess_Inherit] = true;
   132 	g_TraceHandle = handlesys->CreateType("TraceRay", this, 0, &TraceAccess, NULL, myself->GetIdentity(), &err);
   133 	if (g_TraceHandle == 0)
   134 	{
   135 		handlesys->RemoveType(g_CallHandle, myself->GetIdentity());
   136 		g_CallHandle = 0;
   137 		snprintf(error, maxlength, "Could not create traceray handle type (err: %d)", err);
   138 		return false;
   139 	}
   140 
   141 #if SOURCE_ENGINE >= SE_ORANGEBOX
   142 	g_pCVar = icvar;
   143 #endif
   144 	CONVAR_REGISTER(this);
   145 
   146 	SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SDKTools::LevelInit), true);
   147 
   148 	playerhelpers->RegisterCommandTargetProcessor(this);
   149 
   150 	MathLib_Init(2.2f, 2.2f, 0.0f, 2);
   151 
   152 	spengine = g_pSM->GetScriptingEngine();
   153 
   154 	plsys->AddPluginsListener(&g_OutputManager);
   155 
   156 	CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
   157 	g_OutputManager.Init();
   158 
   159 	VoiceInit();
   160 
   161 	GetIServer();
   162 
   163 	GameRulesNativesInit();
   164 
   165 	InitSDKToolsAPI();
   166 
   167 	return true;
   168 }
   169 
   170 void SDKTools::OnHandleDestroy(HandleType_t type, void *object)
   171 {
   172 	if (type == g_CallHandle)
   173 	{
   174 		ValveCall *v = (ValveCall *)object;
   175 		delete v;
   176 	}
   177 	else if (type == g_TraceHandle)
   178 	{
   179 		trace_t *tr = (trace_t *)object;
   180 		delete tr;
   181 	}
   182 }
   183 
   184 void SDKTools::SDK_OnUnload()
   185 {
   186 	SourceHook::List<ValveCall *>::iterator iter;
   187 	for (iter = g_RegCalls.begin();
   188 		 iter != g_RegCalls.end();
   189 		 iter++)
   190 	{
   191 		delete (*iter);
   192 	}
   193 	g_RegCalls.clear();
   194 	ShutdownHelpers();
   195 
   196 	if (g_pAcceptInput)
   197 	{
   198 		g_pAcceptInput->Destroy();
   199 		g_pAcceptInput = NULL;
   200 	}
   201 
   202 	g_TEManager.Shutdown();
   203 	s_TempEntHooks.Shutdown();
   204 	s_SoundHooks.Shutdown();
   205 	g_Hooks.Shutdown();
   206 	g_OutputManager.Shutdown();
   207 
   208 	gameconfs->CloseGameConfigFile(g_pGameConf);
   209 	playerhelpers->RemoveClientListener(&g_SdkTools);
   210 	playerhelpers->UnregisterCommandTargetProcessor(this);
   211 	plsys->RemovePluginsListener(&g_OutputManager);
   212 
   213 	SH_REMOVE_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &SDKTools::LevelInit), true);
   214 
   215 	if (enginePatch)
   216 	{
   217 		SH_RELEASE_CALLCLASS(enginePatch);
   218 		enginePatch = NULL;
   219 	}
   220 	if (enginesoundPatch)
   221 	{
   222 		SH_RELEASE_CALLCLASS(enginesoundPatch);
   223 		enginesoundPatch = NULL;
   224 	}
   225 
   226 	bool err;
   227 	if (g_CallHandle != 0)
   228 	{
   229 		if ((err = handlesys->RemoveType(g_CallHandle, myself->GetIdentity())) != true)
   230 		{
   231 			g_pSM->LogError(myself, "Could not remove call handle (type=%x, err=%d)", g_CallHandle, err);
   232 		}
   233 	}
   234 
   235 	if (g_TraceHandle != 0)
   236 	{
   237 		if ((err = handlesys->RemoveType(g_TraceHandle, myself->GetIdentity())) != true)
   238 		{
   239 			g_pSM->LogError(myself, "Could not remove trace handle (type=%x, err=%d)", g_TraceHandle, err);
   240 		}
   241 	}
   242 }
   243 
   244 bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
   245 {
   246 	GET_V_IFACE_ANY(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
   247 	GET_V_IFACE_ANY(GetEngineFactory, engsound, IEngineSound, IENGINESOUND_SERVER_INTERFACE_VERSION);
   248 	GET_V_IFACE_ANY(GetEngineFactory, enginetrace, IEngineTrace, INTERFACEVERSION_ENGINETRACE_SERVER);
   249 	GET_V_IFACE_ANY(GetEngineFactory, netstringtables, INetworkStringTableContainer, INTERFACENAME_NETWORKSTRINGTABLESERVER);
   250 #if SOURCE_ENGINE != SE_DOTA
   251 	GET_V_IFACE_ANY(GetEngineFactory, pluginhelpers, IServerPluginHelpers, INTERFACEVERSION_ISERVERPLUGINHELPERS);
   252 #endif
   253 	GET_V_IFACE_ANY(GetServerFactory, serverClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS);
   254 	GET_V_IFACE_ANY(GetEngineFactory, voiceserver, IVoiceServer, INTERFACEVERSION_VOICESERVER);
   255 	GET_V_IFACE_ANY(GetServerFactory, playerinfomngr, IPlayerInfoManager, INTERFACEVERSION_PLAYERINFOMANAGER);
   256 	GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
   257 
   258 #if SOURCE_ENGINE >= SE_ORANGEBOX
   259 	GET_V_IFACE_ANY(GetServerFactory, servertools, IServerTools, VSERVERTOOLS_INTERFACE_VERSION);
   260 #endif
   261 
   262 	gpGlobals = ismm->GetCGlobals();
   263 	enginePatch = SH_GET_CALLCLASS(engine);
   264 	enginesoundPatch = SH_GET_CALLCLASS(engsound);
   265 
   266 	return true;
   267 }
   268 
   269 void SDKTools::SDK_OnAllLoaded()
   270 {
   271 	SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
   272 
   273 	if (!g_pBinTools)
   274 	{
   275 		return;
   276 	}
   277 
   278 	g_TEManager.Initialize();
   279 	s_TempEntHooks.Initialize();
   280 	s_SoundHooks.Initialize();
   281 	g_Hooks.Initialize();
   282 	InitializeValveGlobals();
   283 }
   284 
   285 void SDKTools::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
   286 {
   287 	InitTeamNatives();
   288 	GetResourceEntity();
   289 }
   290 
   291 bool SDKTools::QueryRunning(char *error, size_t maxlength)
   292 {
   293 	SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
   294 
   295 	return true;
   296 }
   297 
   298 bool SDKTools::QueryInterfaceDrop(SMInterface *pInterface)
   299 {
   300 	if (pInterface == g_pBinTools)
   301 	{
   302 		return false;
   303 	}
   304 
   305 	return IExtensionInterface::QueryInterfaceDrop(pInterface);
   306 }
   307 
   308 void SDKTools::NotifyInterfaceDrop(SMInterface *pInterface)
   309 {
   310 	SourceHook::List<ValveCall *>::iterator iter;
   311 	for (iter = g_RegCalls.begin();
   312 		iter != g_RegCalls.end();
   313 		iter++)
   314 	{
   315 		delete (*iter);
   316 	}
   317 	g_RegCalls.clear();
   318 	ShutdownHelpers();
   319 
   320 	g_TEManager.Shutdown();
   321 	s_TempEntHooks.Shutdown();
   322 
   323 	if (g_pAcceptInput)
   324 	{
   325 		g_pAcceptInput->Destroy();
   326 		g_pAcceptInput = NULL;
   327 	}
   328 }
   329 
   330 bool SDKTools::RegisterConCommandBase(ConCommandBase *pVar)
   331 {
   332 #if defined METAMOD_PLAPI_VERSION
   333 	return g_SMAPI->RegisterConCommandBase(g_PLAPI, pVar);
   334 #else
   335 	return g_SMAPI->RegisterConCmdBase(g_PLAPI, pVar);
   336 #endif
   337 }
   338 
   339 bool SDKTools::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
   340 {
   341 	const char *name;
   342 	char key[32];
   343 	int count, n = 1;
   344 
   345 	if (!(name=g_pGameConf->GetKeyValue("SlapSoundCount")))
   346 	{
   347 		RETURN_META_VALUE(MRES_IGNORED, true);
   348 	}
   349 
   350 	count = atoi(name);
   351 
   352 	while (n <= count)
   353 	{
   354 		snprintf(key, sizeof(key), "SlapSound%d", n);
   355 		if ((name=g_pGameConf->GetKeyValue(key)))
   356 		{
   357 			engsound->PrecacheSound(name, true);
   358 		}
   359 		n++;
   360 	}
   361 
   362 	RETURN_META_VALUE(MRES_IGNORED, true);
   363 }
   364 
   365 bool SDKTools::ProcessCommandTarget(cmd_target_info_t *info)
   366 {
   367 	IGamePlayer *pAdmin = info->admin ? playerhelpers->GetGamePlayer(info->admin) : NULL;
   368 
   369 	if (strcmp(info->pattern, "@aim") == 0)
   370 	{
   371 		/* The server can't aim, of course. */
   372 		if (pAdmin == NULL)
   373 		{
   374 			return false;
   375 		}
   376 
   377 		int player_index;
   378 		if ((player_index = GetClientAimTarget(pAdmin->GetEdict(), true)) < 1)
   379 		{
   380 			info->reason = COMMAND_TARGET_NONE;
   381 			info->num_targets = 0;
   382 			return true;
   383 		}
   384 
   385 		IGamePlayer *pTarget = playerhelpers->GetGamePlayer(player_index);
   386 
   387 		if (pTarget == NULL)
   388 		{
   389 			info->reason = COMMAND_TARGET_NONE;
   390 			info->num_targets = 0;
   391 			return true;
   392 		}
   393 
   394 		info->reason = playerhelpers->FilterCommandTarget(pAdmin, pTarget, info->flags);
   395 		if (info->reason != COMMAND_TARGET_VALID)
   396 		{
   397 			info->num_targets = 0;
   398 			return true;
   399 		}
   400 
   401 		info->targets[0] = player_index;
   402 		info->num_targets = 1;
   403 		info->reason = COMMAND_TARGET_VALID;
   404 		info->target_name_style = COMMAND_TARGETNAME_RAW;
   405 		snprintf(info->target_name, info->target_name_maxlength, "%s", pTarget->GetName());
   406 		return true;
   407 	}
   408 	else if (strcmp(info->pattern, "@spec") == 0)
   409 	{
   410 		const char *teamname = tools_GetTeamName(1);
   411 		if (strcasecmp(teamname, "spectator") != 0)
   412 			return false;
   413 		info->num_targets = 0;
   414 		for (int i = 1; i <= playerhelpers->GetMaxClients(); i++)
   415 		{
   416 			IGamePlayer *player = playerhelpers->GetGamePlayer(i);
   417 			if (player == NULL || !player->IsInGame())
   418 				continue;
   419 			IPlayerInfo *plinfo = player->GetPlayerInfo();
   420 			if (plinfo == NULL)
   421 				continue;
   422 			if (plinfo->GetTeamIndex() == 1 &&
   423 			    playerhelpers->FilterCommandTarget(pAdmin, player, info->flags) ==
   424 				COMMAND_TARGET_VALID)
   425 			{
   426 				info->targets[info->num_targets++] = i;
   427 			}
   428 		}
   429 		info->reason = info->num_targets > 0 ? COMMAND_TARGET_VALID : COMMAND_TARGET_EMPTY_FILTER;
   430 		info->target_name_style = COMMAND_TARGETNAME_ML;
   431 		snprintf(info->target_name, info->target_name_maxlength, "all spectators");
   432 		return true;
   433 	}
   434 
   435 	return false;
   436 }
   437 
   438 const char *SDKTools::GetExtensionVerString()
   439 {
   440 	return SOURCEMOD_VERSION;
   441 }
   442 
   443 const char *SDKTools::GetExtensionDateString()
   444 {
   445 	return SOURCEMOD_BUILD_TIME;
   446 }
   447 
   448 void SDKTools::OnClientPutInServer(int client)
   449 {
   450 	g_Hooks.OnClientPutInServer(client);
   451 }
   452 
   453 class SDKTools_API : public ISDKTools
   454 {
   455 public:
   456 	virtual const char *GetInterfaceName()
   457 	{
   458 		return SMINTERFACE_SDKTOOLS_NAME;
   459 	}
   460 
   461 	virtual unsigned int GetInterfaceVersion()
   462 	{
   463 		return SMINTERFACE_SDKTOOLS_VERSION;
   464 	}
   465 
   466 	virtual IServer *GetIServer()
   467 	{
   468 		return iserver;
   469 	}
   470 	
   471 	virtual void *GetGameRules()
   472 	{
   473 		if (!g_pGameRules)
   474 			return NULL;
   475 		
   476 		return *g_pGameRules;
   477 	}
   478 } g_SDKTools_API;
   479 
   480 static void InitSDKToolsAPI()
   481 {
   482 	g_pSDKTools = &g_SDKTools_API;
   483 	sharesys->AddInterface(myself, g_pSDKTools);
   484 }