extensions/tf2/criticals.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Thu Aug 29 09:27:35 2013 -0400 (2013-08-29)
changeset 4092 f184c016f489
parent 4001 608bc5be1ef0
child 4093 da128cd7be77
permissions -rw-r--r--
Clean up TF2 ext CalcCritical code (bug 5894, r=voided).
pred@1979
     1
/**
pred@1979
     2
 * vim: set ts=4 :
pred@1979
     3
 * =============================================================================
ds@1990
     4
 * SourceMod Team Fortress 2 Extension
pred@1979
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
pred@1979
     6
 * =============================================================================
pred@1979
     7
 *
pred@1979
     8
 * This program is free software; you can redistribute it and/or modify it under
pred@1979
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
pred@1979
    10
 * Free Software Foundation.
pred@1979
    11
 * 
pred@1979
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
pred@1979
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
pred@1979
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
pred@1979
    15
 * details.
pred@1979
    16
 *
pred@1979
    17
 * You should have received a copy of the GNU General Public License along with
pred@1979
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
pred@1979
    19
 *
pred@1979
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
pred@1979
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
pred@1979
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
pred@1979
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
pred@1979
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
pred@1979
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
pred@1979
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
pred@1979
    27
 * or <http://www.sourcemod.net/license.php>.
pred@1979
    28
 *
pred@1979
    29
 * Version: $Id$
pred@1979
    30
 */
pred@1979
    31
pred@1979
    32
#include "criticals.h"
flaminsarge@4001
    33
#include "util.h"
pred@1979
    34
skamonkey@4092
    35
CritManager g_CritManager;
skamonkey@4092
    36
skamonkey@4092
    37
IForward *g_critForward = nullptr;
skamonkey@4092
    38
skamonkey@4092
    39
SH_DECL_MANUALHOOK0(CalcIsAttackCriticalHelper, 0, 0, 0, bool);
skamonkey@4092
    40
SH_DECL_MANUALHOOK0(CalcIsAttackCriticalHelperNoCrits, 0, 0, 0, bool);
skamonkey@4092
    41
skamonkey@4092
    42
const char TF_WEAPON_DATATABLE[] = "DT_TFWeaponBase";
pred@1979
    43
skamonkey@4092
    44
CritManager::CritManager() :
skamonkey@4092
    45
	m_enabled(false),
skamonkey@4092
    46
	m_hooksSetup(false)
skamonkey@4092
    47
{
skamonkey@4092
    48
	m_entsHooked.Init();
skamonkey@4092
    49
}
flaminsarge@4001
    50
skamonkey@4092
    51
bool CritManager::TryEnable()
skamonkey@4092
    52
{
skamonkey@4092
    53
	if (!m_hooksSetup)
skamonkey@4092
    54
	{
skamonkey@4092
    55
		int offset;
pred@1979
    56
skamonkey@4092
    57
		if (!g_pGameConf->GetOffset("CalcIsAttackCriticalHelper", &offset))
skamonkey@4092
    58
		{
skamonkey@4092
    59
			g_pSM->LogError(myself, "Failed to find CalcIsAttackCriticalHelper offset");
skamonkey@4092
    60
			return false;
skamonkey@4092
    61
		}
skamonkey@4092
    62
skamonkey@4092
    63
		SH_MANUALHOOK_RECONFIGURE(CalcIsAttackCriticalHelper, offset, 0, 0);
pred@1979
    64
skamonkey@4092
    65
		if (!g_pGameConf->GetOffset("CalcIsAttackCriticalHelperNoCrits", &offset))
skamonkey@4092
    66
		{
skamonkey@4092
    67
			g_pSM->LogError(myself, "Failed to find CalcIsAttackCriticalHelperNoCrits offset");
skamonkey@4092
    68
			return false;
skamonkey@4092
    69
		}
skamonkey@4092
    70
skamonkey@4092
    71
		SH_MANUALHOOK_RECONFIGURE(CalcIsAttackCriticalHelperNoCrits, offset, 0, 0);
skamonkey@4092
    72
skamonkey@4092
    73
		m_hooksSetup = true;
skamonkey@4092
    74
	}
pred@2619
    75
skamonkey@4092
    76
	for (size_t i = playerhelpers->GetMaxClients() + 1; i < MAX_EDICTS; ++i)
pred@1979
    77
	{
skamonkey@4092
    78
		CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i);
skamonkey@4092
    79
		if (pEntity == nullptr)
skamonkey@4092
    80
			continue;
skamonkey@4092
    81
skamonkey@4092
    82
		IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
skamonkey@4092
    83
		IServerNetworkable *pNetworkable = pUnknown->GetNetworkable();
skamonkey@4092
    84
		if (!pNetworkable)
skamonkey@4092
    85
			continue;
skamonkey@4092
    86
skamonkey@4092
    87
		if (!UTIL_ContainsDataTable(pNetworkable->GetServerClass()->m_pTable, TF_WEAPON_DATATABLE))
skamonkey@4092
    88
			continue;
skamonkey@4092
    89
skamonkey@4092
    90
		SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
skamonkey@4092
    91
		SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
skamonkey@4092
    92
skamonkey@4092
    93
		m_entsHooked.Set(i);
pred@1979
    94
	}
pred@1979
    95
skamonkey@4092
    96
	m_enabled = true;
pred@1979
    97
skamonkey@4092
    98
	return true;
skamonkey@4092
    99
}
pred@1979
   100
skamonkey@4092
   101
void CritManager::Disable()
skamonkey@4092
   102
{
skamonkey@4092
   103
	int i = m_entsHooked.FindNextSetBit(playerhelpers->GetMaxClients() + 1);
skamonkey@4092
   104
	for (i; i != -1; i = m_entsHooked.FindNextSetBit(i))
skamonkey@4092
   105
	{
skamonkey@4092
   106
		CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i);
skamonkey@4092
   107
		SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
skamonkey@4092
   108
		SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
pred@1979
   109
skamonkey@4092
   110
		m_entsHooked.Set(i, false);
pred@1979
   111
	}
pred@1979
   112
skamonkey@4092
   113
	m_enabled = false;
skamonkey@4092
   114
}
pred@1979
   115
skamonkey@4092
   116
void CritManager::OnEntityCreated(CBaseEntity *pEntity, const char *classname)
skamonkey@4092
   117
{
skamonkey@4092
   118
	if (!m_enabled)
skamonkey@4092
   119
		return;
pred@1979
   120
skamonkey@4092
   121
	IServerUnknown *pUnknown = (IServerUnknown *)pEntity;
skamonkey@4092
   122
	IServerNetworkable *pNetworkable = pUnknown->GetNetworkable();
skamonkey@4092
   123
	if (!pNetworkable)
skamonkey@4092
   124
		return;
pred@1979
   125
skamonkey@4092
   126
	if (!UTIL_ContainsDataTable(pNetworkable->GetServerClass()->m_pTable, TF_WEAPON_DATATABLE))
skamonkey@4092
   127
		return;
skamonkey@4092
   128
skamonkey@4092
   129
	SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
skamonkey@4092
   130
	SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
skamonkey@4092
   131
skamonkey@4092
   132
	m_entsHooked.Set(gamehelpers->EntityToBCompatRef(pEntity));
pred@1979
   133
}
pred@1979
   134
skamonkey@4092
   135
void CritManager::OnEntityDestroyed(CBaseEntity *pEntity)
pred@1979
   136
{
skamonkey@4092
   137
	if (!m_enabled)
skamonkey@4092
   138
		return;
skamonkey@4092
   139
skamonkey@4092
   140
	int index = gamehelpers->EntityToBCompatRef(pEntity);
skamonkey@4092
   141
	if (index < 0 || index >= MAX_EDICTS)
skamonkey@4092
   142
		return;
skamonkey@4092
   143
	
skamonkey@4092
   144
	if (!m_entsHooked.IsBitSet(index))
skamonkey@4092
   145
		return;
skamonkey@4092
   146
skamonkey@4092
   147
	SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
skamonkey@4092
   148
	SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false);
pred@2619
   149
skamonkey@4092
   150
	m_entsHooked.Set(index, false);
skamonkey@4092
   151
}
skamonkey@4092
   152
skamonkey@4092
   153
bool CritManager::Hook_CalcIsAttackCriticalHelpers()
skamonkey@4092
   154
{
skamonkey@4092
   155
	CBaseEntity *pWeapon = META_IFACEPTR(CBaseEntity);
skamonkey@4092
   156
	
skamonkey@4092
   157
	// If there's an invalid ent or invalid networkable here, we've got issues elsewhere.
skamonkey@4092
   158
skamonkey@4092
   159
	IServerNetworkable *pNetWeapon = ((IServerUnknown *)pWeapon)->GetNetworkable();
skamonkey@4092
   160
	ServerClass *pServerClass = pNetWeapon->GetServerClass();
skamonkey@4092
   161
	if (!pServerClass)
pred@1979
   162
	{
skamonkey@4092
   163
		g_pSM->LogError(myself, "Invalid server class on weapon.");
skamonkey@4092
   164
		RETURN_META_VALUE(MRES_IGNORED, false);
pred@1979
   165
	}
pred@1979
   166
pred@1979
   167
	sm_sendprop_info_t info;
skamonkey@4092
   168
	if (!gamehelpers->FindSendPropInfo(pServerClass->GetName(), "m_hOwnerEntity", &info))
pred@1979
   169
	{
skamonkey@4092
   170
		g_pSM->LogError(myself, "Could not find m_hOwnerEntity on %s", pServerClass->GetName());
skamonkey@4092
   171
		RETURN_META_VALUE(MRES_IGNORED, false);
pred@1979
   172
	}
pred@2136
   173
skamonkey@4092
   174
	int returnValue = 0;
pred@1979
   175
skamonkey@4092
   176
	int ownerIndex = -1;
skamonkey@4092
   177
	CBaseHandle &hndl = *(CBaseHandle *) ((intptr_t)pWeapon + info.actual_offset);
skamonkey@4092
   178
	CBaseEntity *pHandleEntity = gamehelpers->ReferenceToEntity(hndl.GetEntryIndex());
skamonkey@4092
   179
skamonkey@4092
   180
	if (pHandleEntity != nullptr && hndl == reinterpret_cast<IHandleEntity *>(pHandleEntity)->GetRefEHandle())
flaminsarge@4001
   181
	{
skamonkey@4092
   182
		ownerIndex = hndl.GetEntryIndex();
flaminsarge@4001
   183
	}
flaminsarge@4001
   184
skamonkey@4092
   185
	g_critForward->PushCell(ownerIndex); //Client index
skamonkey@4092
   186
	g_critForward->PushCell(gamehelpers->EntityToBCompatRef(pWeapon)); // Weapon index
skamonkey@4092
   187
	g_critForward->PushString(gamehelpers->GetEntityClassname(pWeapon)); //Weapon classname
pred@2136
   188
	g_critForward->PushCellByRef(&returnValue); //return value
pred@1979
   189
pred@1979
   190
	cell_t result = 0;
pred@1979
   191
pred@2136
   192
	g_critForward->Execute(&result);
pred@1979
   193
skamonkey@4092
   194
	if (result && returnValue)
flaminsarge@4001
   195
	{
skamonkey@4092
   196
		RETURN_META_VALUE(MRES_SUPERCEDE, true);
skamonkey@3159
   197
	}
flaminsarge@4001
   198
	
skamonkey@4092
   199
	RETURN_META_VALUE(MRES_IGNORED, false);
pred@2136
   200
}