extensions/tf2/extension.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Mon Jun 14 09:32:50 2010 -0400 (2010-06-14)
changeset 3041 a0587dee87ae
parent 2790 6dd851ba15fd
child 3056 4e1591dceb8d
permissions -rw-r--r--
Fixed outputname null pointer crash in sdktools (bug 4422, r=fyren)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod Team Fortress 2 Extension
     5  * Copyright (C) 2004-2008 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 "util.h"
    35 #include "RegNatives.h"
    36 #include "iplayerinfo.h"
    37 #include "sm_trie_tpl.h"
    38 #include "criticals.h"
    39 #include "CDetour/detours.h"
    40 
    41 /**
    42  * @file extension.cpp
    43  * @brief Implement extension code here.
    44  */
    45 
    46 
    47 TF2Tools g_TF2Tools;
    48 IGameConfig *g_pGameConf = NULL;
    49 
    50 IBinTools *g_pBinTools = NULL;
    51 
    52 SMEXT_LINK(&g_TF2Tools);
    53 
    54 ICvar *icvar = NULL;
    55 
    56 CGlobalVars *gpGlobals = NULL;
    57 
    58 sm_sendprop_info_t *playerSharedOffset;
    59 
    60 extern sp_nativeinfo_t g_TFNatives[];
    61 
    62 int g_resourceEntity;
    63 
    64 SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int , int);
    65 
    66 bool TF2Tools::SDK_OnLoad(char *error, size_t maxlength, bool late)
    67 {
    68 	if (strcmp(g_pSM->GetGameFolderName(), "tf") != 0)
    69 	{
    70 		UTIL_Format(error, maxlength, "Cannot Load TF2 Extension on mods other than TF2");
    71 		return false;
    72 	}
    73 
    74 	ServerClass *sc = UTIL_FindServerClass("CTFPlayer");
    75 	if (sc == NULL)
    76 	{
    77 		UTIL_Format(error, maxlength, "Could not find CTFPlayer server class");
    78 		return false;
    79 	}
    80 
    81 	playerSharedOffset = new sm_sendprop_info_t;
    82 
    83 	if (!UTIL_FindDataTable(sc->m_pTable, "DT_TFPlayerShared", playerSharedOffset, 0))
    84 	{
    85 		UTIL_Format(error, maxlength, "Could not find DT_TFPlayerShared data table");
    86 		return false;
    87 	}
    88 
    89 	sharesys->AddDependency(myself, "bintools.ext", true, true);
    90 
    91 	char conf_error[255] = "";
    92 	if (!gameconfs->LoadGameConfigFile("sm-tf2.games", &g_pGameConf, conf_error, sizeof(conf_error)))
    93 	{
    94 		if (conf_error[0])
    95 		{
    96 			UTIL_Format(error, maxlength, "Could not read sm-tf2.games.txt: %s", conf_error);
    97 		}
    98 		return false;
    99 	}
   100 
   101 	CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
   102 
   103 	sharesys->AddNatives(myself, g_TFNatives);
   104 	sharesys->RegisterLibrary(myself, "tf2");
   105 
   106 	plsys->AddPluginsListener(this);
   107 
   108 	playerhelpers->RegisterCommandTargetProcessor(this);
   109 
   110 	g_critForward = forwards->CreateForward("TF2_CalcIsAttackCritical", ET_Hook, 4, NULL, Param_Cell, Param_Cell, Param_String, Param_CellByRef);
   111 
   112 	g_pCVar = icvar;
   113 
   114 	m_DetoursEnabled = false;
   115 
   116 	return true;
   117 }
   118 
   119 const char *TF2Tools::GetExtensionVerString()
   120 {
   121 	return SM_FULL_VERSION;
   122 }
   123 
   124 const char *TF2Tools::GetExtensionDateString()
   125 {
   126 	return SM_BUILD_TIMESTAMP;
   127 }
   128 
   129 bool TF2Tools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
   130 {
   131 	GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
   132 
   133 	gpGlobals = ismm->GetCGlobals();
   134 
   135 	SH_ADD_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_STATIC(OnServerActivate), true);
   136 
   137 	GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
   138 
   139 	GET_V_IFACE_CURRENT(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
   140 
   141 	GET_V_IFACE_CURRENT(GetEngineFactory, m_GameEventManager, IGameEventManager2, INTERFACEVERSION_GAMEEVENTSMANAGER2);
   142 	m_GameEventManager->AddListener(this, "teamplay_restart_round", true);
   143 
   144 	return true;
   145 }
   146 
   147 void TF2Tools::SDK_OnUnload()
   148 {
   149 	SH_REMOVE_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_STATIC(OnServerActivate), true);
   150 
   151 	g_RegNatives.UnregisterAll();
   152 	gameconfs->CloseGameConfigFile(g_pGameConf);
   153 	playerhelpers->UnregisterCommandTargetProcessor(this);
   154 
   155 	plsys->RemovePluginsListener(this);
   156 
   157 	forwards->ReleaseForward(g_critForward);
   158 }
   159 
   160 void TF2Tools::SDK_OnAllLoaded()
   161 {
   162 	SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
   163 }
   164 
   165 bool TF2Tools::RegisterConCommandBase(ConCommandBase *pVar)
   166 {
   167 	return g_SMAPI->RegisterConCommandBase(g_PLAPI, pVar);
   168 }
   169 
   170 void TF2Tools::FireGameEvent( IGameEvent *event )
   171 {
   172 	timersys->NotifyOfGameStart();
   173 	timersys->MapTimeLeftChanged();
   174 }
   175 
   176 bool TF2Tools::QueryRunning(char *error, size_t maxlength)
   177 {
   178 	SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
   179 
   180 	return true;
   181 }
   182 
   183 bool TF2Tools::QueryInterfaceDrop(SMInterface *pInterface)
   184 {
   185 	if (pInterface == g_pBinTools)
   186 	{
   187 		return false;
   188 	}
   189 
   190 	return IExtensionInterface::QueryInterfaceDrop(pInterface);
   191 }
   192 
   193 void TF2Tools::NotifyInterfaceDrop(SMInterface *pInterface)
   194 {
   195 	g_RegNatives.UnregisterAll();
   196 }
   197 
   198 void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax)
   199 {
   200 	g_resourceEntity = FindResourceEntity();
   201 }
   202 
   203 bool TF2Tools::ProcessCommandTarget(cmd_target_info_t *info)
   204 {
   205 	int max_clients;
   206 	IPlayerInfo *pInfo;
   207 	unsigned int team_index = 0;
   208 	IGamePlayer *pPlayer, *pAdmin;
   209 
   210 	if ((info->flags & COMMAND_FILTER_NO_MULTI) == COMMAND_FILTER_NO_MULTI)
   211 	{
   212 		return false;
   213 	}
   214 
   215 	if (info->admin)
   216 	{
   217 		if ((pAdmin = playerhelpers->GetGamePlayer(info->admin)) == NULL)
   218 		{
   219 			return false;
   220 		}
   221 		if (!pAdmin->IsInGame())
   222 		{
   223 			return false;
   224 		}
   225 	}
   226 	else
   227 	{
   228 		pAdmin = NULL;
   229 	}
   230 
   231 	if (strcmp(info->pattern, "@red") == 0 )
   232 	{
   233 		team_index = 2;
   234 	}
   235 	else if (strcmp(info->pattern, "@blue") == 0)
   236 	{
   237 		team_index = 3;
   238 	}
   239 	else
   240 	{
   241 		return false;
   242 	}
   243 
   244 	info->num_targets = 0;
   245 
   246 	max_clients = playerhelpers->GetMaxClients();
   247 	for (int i = 1; 
   248 		 i <= max_clients && (cell_t)info->num_targets < info->max_targets; 
   249 		 i++)
   250 	{
   251 		if ((pPlayer = playerhelpers->GetGamePlayer(i)) == NULL)
   252 		{
   253 			continue;
   254 		}
   255 		if (!pPlayer->IsInGame())
   256 		{
   257 			continue;
   258 		}
   259 		if ((pInfo = pPlayer->GetPlayerInfo()) == NULL)
   260 		{
   261 			continue;
   262 		}
   263 		if (pInfo->GetTeamIndex() != (int)team_index)
   264 		{
   265 			continue;
   266 		}
   267 		if (playerhelpers->FilterCommandTarget(pAdmin, pPlayer, info->flags) 
   268 			!= COMMAND_TARGET_VALID)
   269 		{
   270 			continue;
   271 		}
   272 		info->targets[info->num_targets] = i;
   273 		info->num_targets++;
   274 	}
   275 
   276 	if (info->num_targets == 0)
   277 	{
   278 		info->reason = COMMAND_TARGET_EMPTY_FILTER;
   279 	}
   280 	else
   281 	{
   282 		info->reason = COMMAND_TARGET_VALID;
   283 	}
   284 
   285 	info->target_name_style = COMMAND_TARGETNAME_RAW;
   286 	if (team_index == 2)
   287 	{
   288 		UTIL_Format(info->target_name, info->target_name_maxlength, "Red Team");
   289 	}
   290 	else if (team_index == 3)
   291 	{
   292 		UTIL_Format(info->target_name, info->target_name_maxlength, "Blue Team");
   293 	}
   294 
   295 	return true;
   296 }
   297 
   298 void TF2Tools::OnPluginLoaded(IPlugin *plugin)
   299 {
   300 	if (!m_DetoursEnabled && g_critForward->GetFunctionCount())
   301 	{
   302 		InitialiseDetours();
   303 		m_DetoursEnabled = true;
   304 	}
   305 }
   306 
   307 void TF2Tools::OnPluginUnloaded(IPlugin *plugin)
   308 {
   309 	if (m_DetoursEnabled && !g_critForward->GetFunctionCount())
   310 	{
   311 		RemoveDetours();
   312 		m_DetoursEnabled = false;
   313 	}
   314 }
   315 int FindResourceEntity()
   316 {
   317 	return FindEntityByNetClass(-1, "CTFPlayerResource");
   318 }
   319 
   320 
   321 int FindEntityByNetClass(int start, const char *classname)
   322 {
   323 	edict_t *current;
   324 
   325 	for (int i = ((start != -1) ? start : 0); i < gpGlobals->maxEntities; i++)
   326 	{
   327 		current = engine->PEntityOfEntIndex(i);
   328 		if (current == NULL)
   329 		{
   330 			continue;
   331 		}
   332 
   333 		IServerNetworkable *network = current->GetNetworkable();
   334 
   335 		if (network == NULL)
   336 		{
   337 			continue;
   338 		}
   339 
   340 		ServerClass *sClass = network->GetServerClass();
   341 		const char *name = sClass->GetName();
   342 		
   343 
   344 		if (strcmp(name, classname) == 0)
   345 		{
   346 			return i;
   347 		}
   348 	}
   349 
   350 	return -1;
   351 }
   352 
   353 TFClassType ClassnameToType(const char *classname)
   354 {
   355 	static KTrie<TFClassType> trie;
   356 	static bool filled = false;
   357 	
   358 	if (!filled)
   359 	{
   360 		trie.insert("scout", TFClass_Scout);
   361 		trie.insert("sniper", TFClass_Sniper);
   362 		trie.insert("soldier", TFClass_Soldier);
   363 		trie.insert("demoman", TFClass_DemoMan);
   364 		trie.insert("demo", TFClass_DemoMan);
   365 		trie.insert("medic", TFClass_Medic);
   366 		trie.insert("heavy", TFClass_Heavy);
   367 		trie.insert("hwg", TFClass_Heavy);
   368 		trie.insert("pyro", TFClass_Pyro);
   369 		trie.insert("spy", TFClass_Spy);
   370 		trie.insert("engineer", TFClass_Engineer);
   371 		trie.insert("engy", TFClass_Engineer);
   372 
   373 		filled = true;	
   374 	}
   375 	
   376 	TFClassType *value;
   377 
   378 	if (!(value = trie.retrieve(classname)))
   379 	{
   380 		return TFClass_Unknown;	
   381 	}
   382 
   383 	return *value;
   384 }
   385 
   386 
   387 
   388 /**
   389  * A picture of a blue crab given to me as a gift and stored here for safe keeping
   390  *
   391  * http://www.democracycellproject.net/blog/archives/Clown%20car.jpg
   392  */
   393