Added Critical Hit hook to TF2 extension
authorMatt Woodrow <pred@alliedmods.net>
Thu Apr 10 04:49:17 2008 +0000 (2008-04-10)
changeset 1979d36fac1a8203
parent 1978 bd38ac67afe1
child 1980 eb4c45360aaf
Added Critical Hit hook to TF2 extension
extensions/tf2/Makefile
extensions/tf2/criticals.cpp
extensions/tf2/criticals.h
extensions/tf2/detours.h
extensions/tf2/extension.cpp
extensions/tf2/extension.h
extensions/tf2/msvc8/tf2.vcproj
extensions/tf2/sdk/smsdk_config.h
gamedata/sm-tf2.games.txt
plugins/include/tf2.inc
     1.1 --- a/extensions/tf2/Makefile	Thu Apr 10 00:10:45 2008 +0000
     1.2 +++ b/extensions/tf2/Makefile	Thu Apr 10 04:49:17 2008 +0000
     1.3 @@ -17,7 +17,7 @@
     1.4  #Uncomment for Metamod: Source enabled extension
     1.5  USEMETA = true
     1.6  
     1.7 -OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp
     1.8 +OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp criticals.cpp
     1.9  
    1.10  ##############################################
    1.11  ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/extensions/tf2/criticals.cpp	Thu Apr 10 04:49:17 2008 +0000
     2.3 @@ -0,0 +1,422 @@
     2.4 +/**
     2.5 + * vim: set ts=4 :
     2.6 + * =============================================================================
     2.7 + * SourceMod TF2 Extension
     2.8 + * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     2.9 + * =============================================================================
    2.10 + *
    2.11 + * This program is free software; you can redistribute it and/or modify it under
    2.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    2.13 + * Free Software Foundation.
    2.14 + * 
    2.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    2.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    2.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    2.18 + * details.
    2.19 + *
    2.20 + * You should have received a copy of the GNU General Public License along with
    2.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    2.22 + *
    2.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    2.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    2.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    2.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    2.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    2.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    2.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    2.30 + * or <http://www.sourcemod.net/license.php>.
    2.31 + *
    2.32 + * Version: $Id$
    2.33 + */
    2.34 +
    2.35 +#include "criticals.h"
    2.36 +
    2.37 +ISourcePawnEngine *spengine = NULL;
    2.38 +CriticalHitManager g_CriticalHitManager;
    2.39 +IServerGameEnts *gameents = NULL;
    2.40 +
    2.41 +int g_returnvalue;
    2.42 +
    2.43 +bool CriticalHitManager::CreateCriticalDetour()
    2.44 +{
    2.45 +	if (!g_pGameConf->GetMemSig("CalcCritical", &critical_address) || !critical_address)
    2.46 +	{
    2.47 +		g_pSM->LogError(myself, "Could not locate CalcCritical - Disabling Critical Hit forward");
    2.48 +		return false;
    2.49 +	}
    2.50 +
    2.51 +	if (!g_pGameConf->GetOffset("CalcCriticalBackup", (int *)&(critical_restore.bytes)))
    2.52 +	{
    2.53 +		g_pSM->LogError(myself, "Could not locate CalcCriticalBackup - Disabling Critical Hit forward");
    2.54 +		return false;
    2.55 +	}
    2.56 +
    2.57 +	/* First, save restore bits */
    2.58 +	for (size_t i=0; i<critical_restore.bytes; i++)
    2.59 +	{
    2.60 +		critical_restore.patch[i] = ((unsigned char *)critical_address)[i];
    2.61 +	}
    2.62 +
    2.63 +	critical_callback = spengine->ExecAlloc(100);
    2.64 +	JitWriter wr;
    2.65 +	JitWriter *jit = &wr;
    2.66 +	wr.outbase = (jitcode_t)critical_callback;
    2.67 +	wr.outptr = wr.outbase;
    2.68 +
    2.69 +	/* Function we are detouring into is
    2.70 +	 *
    2.71 +	 * void CriticalDetour(CTFWeaponBase(void *) *pWeapon)
    2.72 +	 *
    2.73 +	 * push		pWeapon [ecx]
    2.74 +	 */
    2.75 +
    2.76 +#if defined PLATFORM_WINDOWS
    2.77 +	IA32_Push_Reg(jit, REG_ECX);
    2.78 +#elif defined PLATFORM_LINUX
    2.79 +	IA32_Push_Rm_Disp8_ESP(jit, 4);
    2.80 +#endif
    2.81 +
    2.82 +	jitoffs_t call = IA32_Call_Imm32(jit, 0); 
    2.83 +	IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour);
    2.84 +
    2.85 +	
    2.86 +#if defined PLATFORM_LINUX
    2.87 +	IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);		//add esp, 4
    2.88 +#elif defined PLATFORM_WINDOWS
    2.89 +	IA32_Pop_Reg(jit, REG_ECX);
    2.90 +#endif
    2.91 +
    2.92 +	//If TempDetour returns non-zero we want to load something into eax and return this value
    2.93 +
    2.94 +	//IA32_Test_Rm_Reg(jit, eax, eax, something);
    2.95 +	jit->write_ubyte(0x85);
    2.96 +	jit->write_ubyte(0xC0);
    2.97 +
    2.98 +	//JNZ critical_callback+50
    2.99 +	jit->write_ubyte(0x75); 
   2.100 +	jit->write_ubyte(50-((jit->outptr+1)-jit->outbase));
   2.101 +
   2.102 +	/* Patch old bytes in */
   2.103 +	for (size_t i=0; i<critical_restore.bytes; i++)
   2.104 +	{
   2.105 +		jit->write_ubyte(critical_restore.patch[i]);
   2.106 +	}
   2.107 +
   2.108 +	/* Return to the original function */
   2.109 +	call = IA32_Jump_Imm32(jit, 0);
   2.110 +	IA32_Write_Jump32_Abs(jit, call, (unsigned char *)critical_address + critical_restore.bytes);
   2.111 +	
   2.112 +	wr.outbase = (jitcode_t)critical_callback+50;
   2.113 +	wr.outptr = wr.outbase;
   2.114 +
   2.115 +	//copy g_returnvalue into eax
   2.116 +	jit->write_ubyte(0xA1);
   2.117 +	jit->write_uint32((jit_uint32_t)&g_returnvalue);
   2.118 +
   2.119 +	IA32_Return(jit);
   2.120 +
   2.121 +	return true;
   2.122 +}
   2.123 +
   2.124 +bool CriticalHitManager::CreateCriticalMeleeDetour()
   2.125 +{
   2.126 +	if (!g_pGameConf->GetMemSig("CalcCriticalMelee", &melee_address) || !melee_address)
   2.127 +	{
   2.128 +		g_pSM->LogError(myself, "Could not locate CalcCriticalMelee - Disabling Critical Hit forward");
   2.129 +		return false;
   2.130 +	}
   2.131 +
   2.132 +	if (!g_pGameConf->GetOffset("CalcCriticalMeleeBackup", (int *)&(melee_restore.bytes)))
   2.133 +	{
   2.134 +		g_pSM->LogError(myself, "Could not locate CalcCriticalMeleeBackup - Disabling Critical Hit forward");
   2.135 +		return false;
   2.136 +	}
   2.137 +
   2.138 +	/* First, save restore bits */
   2.139 +	for (size_t i=0; i<melee_restore.bytes; i++)
   2.140 +	{
   2.141 +		melee_restore.patch[i] = ((unsigned char *)melee_address)[i];
   2.142 +	}
   2.143 +
   2.144 +	melee_callback = spengine->ExecAlloc(100);
   2.145 +	JitWriter wr;
   2.146 +	JitWriter *jit = &wr;
   2.147 +	wr.outbase = (jitcode_t)melee_callback;
   2.148 +	wr.outptr = wr.outbase;
   2.149 +
   2.150 +	/* Function we are detouring into is
   2.151 +	 *
   2.152 +	 * void CriticalDetour(CTFWeaponBase(void *) *pWeapon)
   2.153 +	 *
   2.154 +	 * push		pWeapon [ecx]
   2.155 +	 */
   2.156 +
   2.157 +#if defined PLATFORM_WINDOWS
   2.158 +	IA32_Push_Reg(jit, REG_ECX);
   2.159 +#elif defined PLATFORM_LINUX
   2.160 +	IA32_Push_Rm_Disp8_ESP(jit, 4);
   2.161 +#endif
   2.162 +
   2.163 +	jitoffs_t call = IA32_Call_Imm32(jit, 0); 
   2.164 +	IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour);
   2.165 +
   2.166 +	
   2.167 +#if defined PLATFORM_LINUX
   2.168 +	IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);		//add esp, 4
   2.169 +#elif defined PLATFORM_WINDOWS
   2.170 +	IA32_Pop_Reg(jit, REG_ECX);
   2.171 +#endif
   2.172 +
   2.173 +	//If TempDetour returns non-zero we want to load something into eax and return this value
   2.174 +
   2.175 +	//IA32_Test_Rm_Reg(jit, eax, eax, something);
   2.176 +	jit->write_ubyte(0x85);
   2.177 +	jit->write_ubyte(0xC0);
   2.178 +
   2.179 +	//JNZ critical_callback+50
   2.180 +	jit->write_ubyte(0x75); 
   2.181 +	jit->write_ubyte(50-((jit->outptr+1)-jit->outbase));
   2.182 +
   2.183 +	/* Patch old bytes in */
   2.184 +	for (size_t i=0; i<melee_restore.bytes; i++)
   2.185 +	{
   2.186 +		jit->write_ubyte(melee_restore.patch[i]);
   2.187 +	}
   2.188 +
   2.189 +	/* Return to the original function */
   2.190 +	call = IA32_Jump_Imm32(jit, 0);
   2.191 +	IA32_Write_Jump32_Abs(jit, call, (unsigned char *)melee_address + melee_restore.bytes);
   2.192 +	
   2.193 +	wr.outbase = (jitcode_t)melee_callback+50;
   2.194 +	wr.outptr = wr.outbase;
   2.195 +
   2.196 +	//copy g_returnvalue into eax
   2.197 +	jit->write_ubyte(0xA1);
   2.198 +	jit->write_uint32((jit_uint32_t)&g_returnvalue);
   2.199 +
   2.200 +	IA32_Return(jit);
   2.201 +
   2.202 +	return true;
   2.203 +}
   2.204 +
   2.205 +bool CriticalHitManager::CreateCriticalKnifeDetour()
   2.206 +{
   2.207 +	if (!g_pGameConf->GetMemSig("CalcCriticalKnife", &knife_address) || !knife_address)
   2.208 +	{
   2.209 +		g_pSM->LogError(myself, "Could not locate CalcCriticalKnife - Disabling Critical Hit forward");
   2.210 +		return false;
   2.211 +	}
   2.212 +
   2.213 +	if (!g_pGameConf->GetOffset("CalcCriticalMeleeBackup", (int *)&(knife_restore.bytes)))
   2.214 +	{
   2.215 +		g_pSM->LogError(myself, "Could not locate CalcCriticalKnifeBackup - Disabling Critical Hit forward");
   2.216 +		return false;
   2.217 +	}
   2.218 +
   2.219 +	/* First, save restore bits */
   2.220 +	for (size_t i=0; i<knife_restore.bytes; i++)
   2.221 +	{
   2.222 +		knife_restore.patch[i] = ((unsigned char *)knife_address)[i];
   2.223 +	}
   2.224 +
   2.225 +	knife_callback = spengine->ExecAlloc(100);
   2.226 +	JitWriter wr;
   2.227 +	JitWriter *jit = &wr;
   2.228 +	wr.outbase = (jitcode_t)knife_callback;
   2.229 +	wr.outptr = wr.outbase;
   2.230 +
   2.231 +	/* Function we are detouring into is
   2.232 +	 *
   2.233 +	 * void CriticalDetour(CTFWeaponBase(void *) *pWeapon)
   2.234 +	 *
   2.235 +	 * push		pWeapon [ecx]
   2.236 +	 */
   2.237 +
   2.238 +#if defined PLATFORM_WINDOWS
   2.239 +	IA32_Push_Reg(jit, REG_ECX);
   2.240 +#elif defined PLATFORM_LINUX
   2.241 +	IA32_Push_Rm_Disp8_ESP(jit, 4);
   2.242 +#endif
   2.243 +
   2.244 +	jitoffs_t call = IA32_Call_Imm32(jit, 0); 
   2.245 +	IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour);
   2.246 +
   2.247 +	
   2.248 +#if defined PLATFORM_LINUX
   2.249 +	IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);		//add esp, 4
   2.250 +#elif defined PLATFORM_WINDOWS
   2.251 +	IA32_Pop_Reg(jit, REG_ECX);
   2.252 +#endif
   2.253 +
   2.254 +	//If TempDetour returns non-zero we want to load something into eax and return this value
   2.255 +
   2.256 +	//IA32_Test_Rm_Reg(jit, eax, eax, something);
   2.257 +	jit->write_ubyte(0x85);
   2.258 +	jit->write_ubyte(0xC0);
   2.259 +
   2.260 +	//JNZ critical_callback+50
   2.261 +	jit->write_ubyte(0x75); 
   2.262 +	jit->write_ubyte(50-((jit->outptr+1)-jit->outbase));
   2.263 +
   2.264 +	/* Patch old bytes in */
   2.265 +	for (size_t i=0; i<knife_restore.bytes; i++)
   2.266 +	{
   2.267 +		jit->write_ubyte(knife_restore.patch[i]);
   2.268 +	}
   2.269 +
   2.270 +	/* Return to the original function */
   2.271 +	call = IA32_Jump_Imm32(jit, 0);
   2.272 +	IA32_Write_Jump32_Abs(jit, call, (unsigned char *)knife_address + knife_restore.bytes);
   2.273 +	
   2.274 +	wr.outbase = (jitcode_t)knife_callback+50;
   2.275 +	wr.outptr = wr.outbase;
   2.276 +
   2.277 +	//copy g_returnvalue into eax
   2.278 +	jit->write_ubyte(0xA1);
   2.279 +	jit->write_uint32((jit_uint32_t)&g_returnvalue);
   2.280 +
   2.281 +	IA32_Return(jit);
   2.282 +
   2.283 +	return true;
   2.284 +}
   2.285 +
   2.286 +void CriticalHitManager::EnableCriticalDetour()
   2.287 +{
   2.288 +	if (!detoured)
   2.289 +	{
   2.290 +		DoGatePatch((unsigned char *)critical_address, &critical_callback);
   2.291 +		DoGatePatch((unsigned char *)melee_address, &melee_callback);
   2.292 +		DoGatePatch((unsigned char *)knife_address, &knife_callback);
   2.293 +		detoured = true;
   2.294 +	}
   2.295 +}
   2.296 +
   2.297 +void CriticalHitManager::DeleteCriticalDetour()
   2.298 +{
   2.299 +	if (detoured)
   2.300 +	{
   2.301 +		DisableCriticalDetour();
   2.302 +	}
   2.303 +
   2.304 +	if (critical_callback)
   2.305 +	{
   2.306 +		/* Free the gate */
   2.307 +		spengine->ExecFree(critical_callback);
   2.308 +		critical_callback = NULL;
   2.309 +	}
   2.310 +
   2.311 +	if (melee_callback)
   2.312 +	{
   2.313 +		/* Free the gate */
   2.314 +		spengine->ExecFree(melee_callback);
   2.315 +		melee_callback = NULL;
   2.316 +	}
   2.317 +
   2.318 +	if (knife_callback)
   2.319 +	{
   2.320 +		/* Free the gate */
   2.321 +		spengine->ExecFree(knife_callback);
   2.322 +		knife_callback = NULL;
   2.323 +	}
   2.324 +}
   2.325 +
   2.326 +bool TempDetour(void *pWeapon)
   2.327 +{
   2.328 +	return g_CriticalHitManager.CriticalDetour(pWeapon);
   2.329 +}
   2.330 +
   2.331 +void CriticalHitManager::DisableCriticalDetour()
   2.332 +{
   2.333 +	if (critical_callback)
   2.334 +	{
   2.335 +		/* Remove the patch */
   2.336 +		ApplyPatch(critical_address, 0, &critical_restore, NULL);
   2.337 +		detoured = false;
   2.338 +	}
   2.339 +
   2.340 +	if (melee_callback)
   2.341 +	{
   2.342 +		/* Remove the patch */
   2.343 +		ApplyPatch(melee_address, 0, &melee_restore, NULL);
   2.344 +		detoured = false;
   2.345 +	}
   2.346 +
   2.347 +	if (melee_callback)
   2.348 +	{
   2.349 +		/* Remove the patch */
   2.350 +		ApplyPatch(knife_address, 0, &knife_restore, NULL);
   2.351 +		detoured = false;
   2.352 +	}
   2.353 +}
   2.354 +
   2.355 +int CheckBaseHandle(CBaseHandle &hndl)
   2.356 +{
   2.357 +	if (!hndl.IsValid())
   2.358 +	{
   2.359 +		return -1;
   2.360 +	}
   2.361 +
   2.362 +	int index = hndl.GetEntryIndex();
   2.363 +
   2.364 +	edict_t *pStoredEdict;
   2.365 +
   2.366 +	pStoredEdict = engine->PEntityOfEntIndex(index);
   2.367 +
   2.368 +	if (pStoredEdict == NULL)
   2.369 +	{
   2.370 +		return -1;
   2.371 +	}
   2.372 +
   2.373 +	IServerEntity *pSE = pStoredEdict->GetIServerEntity();
   2.374 +
   2.375 +	if (pSE == NULL)
   2.376 +	{
   2.377 +		return -1;
   2.378 +	}
   2.379 +
   2.380 +	if (pSE->GetRefEHandle() != hndl)
   2.381 +	{
   2.382 +		return -1;
   2.383 +	}
   2.384 +
   2.385 +	return index;
   2.386 +}
   2.387 +
   2.388 +bool CriticalHitManager::CriticalDetour(void *pWeapon)
   2.389 +{
   2.390 +	edict_t *pEdict = gameents->BaseEntityToEdict((CBaseEntity *)pWeapon);
   2.391 +	
   2.392 +	if (!pEdict)
   2.393 +	{
   2.394 +		g_pSM->LogMessage(myself, "Entity Error");
   2.395 +		return false;
   2.396 +	}
   2.397 +
   2.398 +	sm_sendprop_info_t info;
   2.399 +
   2.400 +	if (!gamehelpers->FindSendPropInfo(pEdict->GetNetworkable()->GetServerClass()->GetName(), "m_hOwnerEntity", &info))
   2.401 +	{
   2.402 +		g_pSM->LogMessage(myself, "Offset Error");
   2.403 +		return false;
   2.404 +	}
   2.405 +
   2.406 +	if (!forward)
   2.407 +	{
   2.408 +		g_pSM->LogMessage(myself, "Invalid Forward");
   2.409 +		return false;
   2.410 +	}
   2.411 +	
   2.412 +	CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pWeapon + info.actual_offset);
   2.413 +	int index = CheckBaseHandle(hndl);
   2.414 +
   2.415 +	forward->PushCell(index); //Client index
   2.416 +	forward->PushCell(engine->IndexOfEdict(pEdict)); // Weapon index
   2.417 +	forward->PushString(pEdict->GetClassName()); //Weapon classname
   2.418 +	forward->PushCellByRef(&g_returnvalue); //return value
   2.419 +
   2.420 +	cell_t result = 0;
   2.421 +
   2.422 +	forward->Execute(&result);
   2.423 +
   2.424 +	return !!result;
   2.425 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/extensions/tf2/criticals.h	Thu Apr 10 04:49:17 2008 +0000
     3.3 @@ -0,0 +1,125 @@
     3.4 +/**
     3.5 + * vim: set ts=4 :
     3.6 + * =============================================================================
     3.7 + * SourceMod TF2 Extension
     3.8 + * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     3.9 + * =============================================================================
    3.10 + *
    3.11 + * This program is free software; you can redistribute it and/or modify it under
    3.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    3.13 + * Free Software Foundation.
    3.14 + * 
    3.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    3.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    3.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    3.18 + * details.
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License along with
    3.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    3.22 + *
    3.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    3.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    3.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    3.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    3.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    3.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    3.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    3.30 + * or <http://www.sourcemod.net/license.php>.
    3.31 + *
    3.32 + * Version: $Id$
    3.33 + */
    3.34 +
    3.35 +#include "extension.h"
    3.36 +#include <jit/jit_helpers.h>
    3.37 +#include <jit/x86/x86_macros.h>
    3.38 +#include "detours.h"
    3.39 +
    3.40 +class CriticalHitManager
    3.41 +{
    3.42 +public:
    3.43 +	CriticalHitManager()
    3.44 +	{
    3.45 +		enabled = false;
    3.46 +		detoured = false;
    3.47 +		critical_address = NULL;
    3.48 +		critical_callback = NULL;
    3.49 +
    3.50 +		melee_address = NULL;
    3.51 +		melee_callback = NULL;
    3.52 +
    3.53 +		knife_address = NULL;
    3.54 +		knife_callback = NULL;
    3.55 +
    3.56 +		forward = NULL;
    3.57 +	}
    3.58 +
    3.59 +	~CriticalHitManager()
    3.60 +	{
    3.61 +		forwards->ReleaseForward(forward);
    3.62 +		DeleteCriticalDetour();
    3.63 +	}
    3.64 +
    3.65 +	void Init()
    3.66 +	{
    3.67 +		if (!CreateCriticalDetour() || !CreateCriticalMeleeDetour() || !CreateCriticalKnifeDetour())
    3.68 +		{
    3.69 +			enabled = false;
    3.70 +			return;
    3.71 +		}
    3.72 +
    3.73 +		forward = forwards->CreateForward("TF2_CalcIsAttackCritical", ET_Hook, 4, NULL, Param_Cell, Param_Cell, Param_String, Param_CellByRef);
    3.74 +
    3.75 +		if (!forward)
    3.76 +		{
    3.77 +			g_pSM->LogError(myself, "Failed to create forward - Disabling critical hit hook");
    3.78 +			enabled = false;
    3.79 +			return;
    3.80 +		}
    3.81 +
    3.82 +		/* TODO: Only enable this once forwards exist. Requires IForwardListener functionality */
    3.83 +		EnableCriticalDetour();
    3.84 +
    3.85 +		enabled = true;
    3.86 +	}
    3.87 +
    3.88 +	bool IsEnabled()
    3.89 +	{
    3.90 +		return enabled;
    3.91 +	}
    3.92 +
    3.93 +	bool CriticalDetour(void *pWeapon);
    3.94 +
    3.95 +private:
    3.96 +	IForward *forward;
    3.97 +
    3.98 +	/* These create/delete the allocated memory */
    3.99 +	bool CreateCriticalDetour();
   3.100 +	bool CreateCriticalMeleeDetour();
   3.101 +	bool CreateCriticalKnifeDetour();
   3.102 +	void DeleteCriticalDetour();
   3.103 +
   3.104 +	/* These patch/unpatch the server.dll */
   3.105 +	void EnableCriticalDetour();
   3.106 +	void DisableCriticalDetour();
   3.107 +
   3.108 +	bool enabled;
   3.109 +	bool detoured;
   3.110 +
   3.111 +	patch_t critical_restore;
   3.112 +	void *critical_address;
   3.113 +	void *critical_callback;
   3.114 +
   3.115 +	patch_t melee_restore;
   3.116 +	void *melee_address;
   3.117 +	void *melee_callback;
   3.118 +
   3.119 +	patch_t knife_restore;
   3.120 +	void *knife_address;
   3.121 +	void *knife_callback;
   3.122 +};
   3.123 +
   3.124 +bool TempDetour(void *pWeapon);
   3.125 +
   3.126 +extern ISourcePawnEngine *spengine;
   3.127 +extern IServerGameEnts *gameents;
   3.128 +extern CriticalHitManager g_CriticalHitManager;
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/extensions/tf2/detours.h	Thu Apr 10 04:49:17 2008 +0000
     4.3 @@ -0,0 +1,98 @@
     4.4 +/**
     4.5 + * vim: set ts=4 :
     4.6 + * =============================================================================
     4.7 + * SourceMod SDKTools Extension
     4.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     4.9 + * =============================================================================
    4.10 + *
    4.11 + * This program is free software; you can redistribute it and/or modify it under
    4.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    4.13 + * Free Software Foundation.
    4.14 + * 
    4.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    4.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    4.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    4.18 + * details.
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License along with
    4.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    4.22 + *
    4.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    4.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    4.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    4.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    4.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    4.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    4.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    4.30 + * or <http://www.sourcemod.net/license.php>.
    4.31 + *
    4.32 + * Version: $Id$
    4.33 + */
    4.34 +
    4.35 +#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_
    4.36 +#define _INCLUDE_SOURCEMOD_DETOURS_H_
    4.37 +
    4.38 +#if defined PLATFORM_LINUX
    4.39 +#include <sys/mman.h>
    4.40 +#define	PAGE_SIZE	4096
    4.41 +#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
    4.42 +#define	PAGE_EXECUTE_READWRITE	PROT_READ|PROT_WRITE|PROT_EXEC
    4.43 +#endif
    4.44 +
    4.45 +struct patch_t
    4.46 +{
    4.47 +	patch_t()
    4.48 +	{
    4.49 +		patch[0] = 0;
    4.50 +		bytes = 0;
    4.51 +	}
    4.52 +	unsigned char patch[20];
    4.53 +	size_t bytes;
    4.54 +};
    4.55 +
    4.56 +inline void ProtectMemory(void *addr, int length, int prot)
    4.57 +{
    4.58 +#if defined PLATFORM_LINUX
    4.59 +	void *addr2 = (void *)ALIGN(addr);
    4.60 +	mprotect(addr2, sysconf(_SC_PAGESIZE), prot);
    4.61 +#elif defined PLATFORM_WINDOWS
    4.62 +	DWORD old_prot;
    4.63 +	VirtualProtect(addr, length, prot, &old_prot);
    4.64 +#endif
    4.65 +}
    4.66 +
    4.67 +inline void SetMemPatchable(void *address, size_t size)
    4.68 +{
    4.69 +	ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE);
    4.70 +}
    4.71 +
    4.72 +inline void DoGatePatch(unsigned char *target, void *callback)
    4.73 +{
    4.74 +	SetMemPatchable(target, 20);
    4.75 +
    4.76 +	target[0] = 0xFF;	/* JMP */
    4.77 +	target[1] = 0x25;	/* MEM32 */
    4.78 +	*(void **)(&target[2]) = callback;
    4.79 +}
    4.80 +
    4.81 +inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore)
    4.82 +{
    4.83 +	ProtectMemory(address, 20, PAGE_EXECUTE_READWRITE);
    4.84 +
    4.85 +	unsigned char *addr = (unsigned char *)address + offset;
    4.86 +	if (restore)
    4.87 +	{
    4.88 +		for (size_t i=0; i<patch->bytes; i++)
    4.89 +		{
    4.90 +			restore->patch[i] = addr[i];
    4.91 +		}
    4.92 +		restore->bytes = patch->bytes;
    4.93 +	}
    4.94 +
    4.95 +	for (size_t i=0; i<patch->bytes; i++)
    4.96 +	{
    4.97 +		addr[i] = patch->patch[i];
    4.98 +	}
    4.99 +}
   4.100 +
   4.101 +#endif //_INCLUDE_SOURCEMOD_DETOURS_H_
     5.1 --- a/extensions/tf2/extension.cpp	Thu Apr 10 00:10:45 2008 +0000
     5.2 +++ b/extensions/tf2/extension.cpp	Thu Apr 10 04:49:17 2008 +0000
     5.3 @@ -34,6 +34,7 @@
     5.4  #include "RegNatives.h"
     5.5  #include "iplayerinfo.h"
     5.6  #include "sm_trie_tpl.h"
     5.7 +#include "criticals.h"
     5.8  
     5.9  /**
    5.10   * @file extension.cpp
    5.11 @@ -48,6 +49,8 @@
    5.12  
    5.13  SMEXT_LINK(&g_TF2Tools);
    5.14  
    5.15 +ICvar *icvar = NULL;
    5.16 +
    5.17  CGlobalVars *gpGlobals = NULL;
    5.18  
    5.19  sm_sendprop_info_t *playerSharedOffset;
    5.20 @@ -94,6 +97,10 @@
    5.21  
    5.22  	playerhelpers->RegisterCommandTargetProcessor(this);
    5.23  
    5.24 +	spengine = g_pSM->GetScriptingEngine();
    5.25 +
    5.26 +	g_pCVar = icvar;
    5.27 +
    5.28  	return true;
    5.29  }
    5.30  
    5.31 @@ -105,6 +112,10 @@
    5.32  
    5.33  	SH_ADD_HOOK(IServerGameDLL, ServerActivate, gamedll, SH_STATIC(OnServerActivate), true);
    5.34  
    5.35 +	GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
    5.36 +
    5.37 +	GET_V_IFACE_CURRENT(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
    5.38 +
    5.39  	return true;
    5.40  }
    5.41  
    5.42 @@ -120,8 +131,16 @@
    5.43  void TF2Tools::SDK_OnAllLoaded()
    5.44  {
    5.45  	SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
    5.46 +
    5.47 +	g_CriticalHitManager.Init();
    5.48  }
    5.49  
    5.50 +bool TF2Tools::RegisterConCommandBase(ConCommandBase *pVar)
    5.51 +{
    5.52 +	return g_SMAPI->RegisterConCommandBase(g_PLAPI, pVar);
    5.53 +}
    5.54 +
    5.55 +
    5.56  bool TF2Tools::QueryRunning(char *error, size_t maxlength)
    5.57  {
    5.58  	SM_CHECK_IFACE(BINTOOLS, g_pBinTools);
    5.59 @@ -291,7 +310,6 @@
    5.60  	if (!filled)
    5.61  	{
    5.62  		trie.insert("scout", TFClass_Scout);
    5.63 -		trie.insert("scout", TFClass_Scout);
    5.64  		trie.insert("sniper", TFClass_Sniper);
    5.65  		trie.insert("soldier", TFClass_Soldier);
    5.66  		trie.insert("demoman", TFClass_DemoMan);
     6.1 --- a/extensions/tf2/extension.h	Thu Apr 10 00:10:45 2008 +0000
     6.2 +++ b/extensions/tf2/extension.h	Thu Apr 10 04:49:17 2008 +0000
     6.3 @@ -46,7 +46,8 @@
     6.4   */
     6.5  class TF2Tools : 
     6.6  	public SDKExtension,
     6.7 -	public ICommandTargetProcessor
     6.8 +	public ICommandTargetProcessor,
     6.9 +	public IConCommandBaseAccessor
    6.10  {
    6.11  public:
    6.12  	/**
    6.13 @@ -88,6 +89,7 @@
    6.14  	bool QueryInterfaceDrop(SMInterface *pInterface);
    6.15  public:
    6.16  	bool ProcessCommandTarget(cmd_target_info_t *info);
    6.17 +	bool RegisterConCommandBase(ConCommandBase *pVar);
    6.18  public:
    6.19  #if defined SMEXT_CONF_METAMOD
    6.20  	/**
     7.1 --- a/extensions/tf2/msvc8/tf2.vcproj	Thu Apr 10 00:10:45 2008 +0000
     7.2 +++ b/extensions/tf2/msvc8/tf2.vcproj	Thu Apr 10 04:49:17 2008 +0000
     7.3 @@ -122,7 +122,7 @@
     7.4  				Name="VCCLCompilerTool"
     7.5  				FavorSizeOrSpeed="1"
     7.6  				AdditionalIncludeDirectories="..;..\sdk;..\..\..\public;..\..\..\public\extensions;..\..\..\public\sourcepawn;&quot;$(HL2SDKOB)&quot;;&quot;$(HL2SDKOB)\public&quot;;&quot;$(HL2SDKOB)\public\engine&quot;;&quot;$(HL2SDKOB)\public\game\server&quot;;&quot;$(HL2SDKOB)\public\tier0&quot;;&quot;$(HL2SDKOB)\public\tier1&quot;;&quot;$(SOURCEMM16)&quot;;&quot;$(SOURCEMM16)\sourcemm&quot;;&quot;$(SOURCEMM16)\sourcehook&quot;"
     7.7 -				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;CSTRIKE_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD"
     7.8 +				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;CSTRIKE_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;ORANGEBOX_BUILD"
     7.9  				RuntimeLibrary="0"
    7.10  				EnableEnhancedInstructionSet="1"
    7.11  				RuntimeTypeInfo="false"
    7.12 @@ -186,6 +186,10 @@
    7.13  			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
    7.14  			>
    7.15  			<File
    7.16 +				RelativePath="..\criticals.cpp"
    7.17 +				>
    7.18 +			</File>
    7.19 +			<File
    7.20  				RelativePath="..\extension.cpp"
    7.21  				>
    7.22  			</File>
    7.23 @@ -208,6 +212,14 @@
    7.24  			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
    7.25  			>
    7.26  			<File
    7.27 +				RelativePath="..\criticals.h"
    7.28 +				>
    7.29 +			</File>
    7.30 +			<File
    7.31 +				RelativePath="..\detours.h"
    7.32 +				>
    7.33 +			</File>
    7.34 +			<File
    7.35  				RelativePath="..\extension.h"
    7.36  				>
    7.37  			</File>
     8.1 --- a/extensions/tf2/sdk/smsdk_config.h	Thu Apr 10 00:10:45 2008 +0000
     8.2 +++ b/extensions/tf2/sdk/smsdk_config.h	Thu Apr 10 04:49:17 2008 +0000
     8.3 @@ -61,7 +61,7 @@
     8.4  #define SMEXT_CONF_METAMOD
     8.5  
     8.6  /** Enable interfaces you want to use here by uncommenting lines */
     8.7 -//#define SMEXT_ENABLE_FORWARDSYS
     8.8 +#define SMEXT_ENABLE_FORWARDSYS
     8.9  //#define SMEXT_ENABLE_HANDLESYS
    8.10  #define SMEXT_ENABLE_PLAYERHELPERS
    8.11  //#define SMEXT_ENABLE_DBMANAGER
     9.1 --- a/gamedata/sm-tf2.games.txt	Thu Apr 10 00:10:45 2008 +0000
     9.2 +++ b/gamedata/sm-tf2.games.txt	Thu Apr 10 04:49:17 2008 +0000
     9.3 @@ -28,6 +28,24 @@
     9.4  				"windows"	"\x56\x8B\xF1\x8B\x4E\x7C\xE8"
     9.5  				"linux"		"@_ZN15CTFPlayerShared8DisguiseEii"
     9.6  			}
     9.7 +			"CalcCritical"
     9.8 +			{
     9.9 +				"library"	"server"
    9.10 +				"linux"		"@_ZN13CTFWeaponBase26CalcIsAttackCriticalHelperEv"
    9.11 +				"windows"	"\x83\xEC\x08\x56\x57\x6A\x00\x68"
    9.12 +			}
    9.13 +			"CalcCriticalMelee"
    9.14 +			{
    9.15 +				"library"	"server"
    9.16 +				"linux"		"@_ZN18CTFWeaponBaseMelee26CalcIsAttackCriticalHelperEv"
    9.17 +				"windows"	"\x83\xEC\x08\x56\xE8\x2A\x2A\x2A\x2A\x8B\xF0\x85\xF6"
    9.18 +			}
    9.19 +			"CalcCriticalKnife"
    9.20 +			{
    9.21 +				"library"	"server"
    9.22 +				"linux"		"@_ZN8CTFKnife26CalcIsAttackCriticalHelperEv"
    9.23 +				"windows"	"\x33\xC0\x83\xB9\x20\x05\x00"
    9.24 +			}
    9.25  
    9.26  		}
    9.27  		"Offsets"
    9.28 @@ -36,7 +54,22 @@
    9.29  			{
    9.30  				"windows"	"279"
    9.31  				"linux"		"280"
    9.32 -			}			
    9.33 +			}
    9.34 +			"CalcCriticalBackup"
    9.35 +			{
    9.36 +				"linux"		"7"
    9.37 +				"windows"	"7"
    9.38 +			}
    9.39 +			"CalcCriticalMeleeBackup"
    9.40 +			{
    9.41 +				"linux"		"7"
    9.42 +				"windows"	"9"
    9.43 +			}
    9.44 +			"CalcCriticalKnifeBackup"
    9.45 +			{
    9.46 +				"linux"		"11"
    9.47 +				"windows"	"9"
    9.48 +			}		
    9.49  		}
    9.50  	}
    9.51  }
    9.52 \ No newline at end of file
    10.1 --- a/plugins/include/tf2.inc	Thu Apr 10 00:10:45 2008 +0000
    10.2 +++ b/plugins/include/tf2.inc	Thu Apr 10 04:49:17 2008 +0000
    10.3 @@ -114,6 +114,23 @@
    10.4  native TFClassType:TF2_GetClass(const String:classname[]);
    10.5  
    10.6  /**
    10.7 + * Called on weapon fire to decide if the current shot should be critical.
    10.8 + * Return Plugin_Continue to let the original calculation or return a higher
    10.9 + * action to override the decision with the value of 'result'
   10.10 + *
   10.11 + * @note Since critical shots are also calculated client side any changes made with
   10.12 + *		 this will not show for the shooter. Projectile weapons such as the rocketlauncher
   10.13 + *		 and demoman weapons will show a critical bullet but no critical sound effect.
   10.14 + *		 Bullet hits should appear as expected.
   10.15 + *
   10.16 + * @param client		Client Index.
   10.17 + * @param weapon		Weapon entity Index.
   10.18 + * @param weaponname	Classname of the weapon.
   10.19 + * @param result		Buffer param for the result of the decision.
   10.20 + */
   10.21 +forward Action:TF2_CalcIsAttackCritical(client, weapon, String:weaponname[], &bool:result)
   10.22 +
   10.23 +/**
   10.24   * Do not edit below this line!
   10.25   */
   10.26  public Extension:__ext_tf2 =