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