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