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