DHooks (Dynamic Hooks - Dev Preview)
1 Attachment(s)
DHooks is now included with SourceMod 1.11.6820. Previous versions are unsupported.
Please use SourceMod's issue tracker on GitHub for any new issues. :bee:
Oh no, not ANOTHER hooks extension!!!
This extension is not just ANOTHER hooks extension :P But rather allows sourcemod plugins to create their own hooks.
Note that you cannot set or get a unknown param/return's value.
As a basic note, if the return type is void there won’t be a return handle pushed to the callback. If there is no params no param handle will be pushed. If this pointer is set to ignore this won’t be pushed.
Example/Test Plugin
PHP Code:
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <dhooks>
// int CBaseCombatCharacter::BloodColor(void)
new Handle:hBloodColor;
// bool CBaseCombatCharacter::Weapon_CanUse(CBaseCombatWeapon *)
new Handle:hHookCanUse;
// Vector CBasePlayer::GetPlayerMaxs()
new Handle:hGetMaxs;
// string_t CBaseEntity::GetModelName(void)
new Handle:hGetModelName;
// bool CGameRules::CanHaveAmmo(CBaseCombatCharacter *, int)
new Handle:hCanHaveAmmo;
// void CBaseEntity::SetModel(char const*)
new Handle:hSetModel;
//float CCSPlayer::GetPlayerMaxSpeed()
new Handle:hGetSpeed;
//int CCSPlayer::OnTakeDamage(CTakeDamageInfo const&)
new Handle:hTakeDamage;
// bool CBaseEntity::AcceptInput(char const*, CBaseEntity*, CBaseEntity*, variant_t, int)
new Handle:hAcceptInput;
//int CBaseCombatCharacter::GiveAmmo(int, int, bool)
new Handle:hGiveAmmo;
// CVEngineServer::ClientPrintf(edict_t *, char const*)
new Handle:hClientPrintf;
public OnPluginStart()
{
new Handle:temp = LoadGameConfigFile("dhooks-test.games");
if(temp == INVALID_HANDLE)
{
SetFailState("Why you no has gamedata?");
}
new offset;
offset = GameConfGetOffset(temp, "BloodColor");
hBloodColor = DHookCreate(offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity, BloodColorPost);
offset = GameConfGetOffset(temp, "GetModelName");
hGetModelName = DHookCreate(offset, HookType_Entity, ReturnType_String, ThisPointer_CBaseEntity, GetModelName);
offset = GameConfGetOffset(temp, "GetMaxs");
hGetMaxs = DHookCreate(offset, HookType_Entity, ReturnType_Vector, ThisPointer_Ignore, GetMaxsPost);
offset = GameConfGetOffset(temp, "CanUse");
hHookCanUse = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, CanUsePost);
DHookAddParam(hHookCanUse, HookParamType_CBaseEntity);
offset = GameConfGetOffset(temp, "CanHaveAmmo");
hCanHaveAmmo = DHookCreate(offset, HookType_GameRules, ReturnType_Bool, ThisPointer_Ignore, CanHaveAmmoPost);
DHookAddParam(hCanHaveAmmo, HookParamType_CBaseEntity);
DHookAddParam(hCanHaveAmmo, HookParamType_Int);
offset = GameConfGetOffset(temp, "SetModel");
hSetModel = DHookCreate(offset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, SetModel);
DHookAddParam(hSetModel, HookParamType_CharPtr);
offset = GameConfGetOffset(temp, "AcceptInput");
hAcceptInput = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, AcceptInput);
DHookAddParam(hAcceptInput, HookParamType_CharPtr);
DHookAddParam(hAcceptInput, HookParamType_CBaseEntity);
DHookAddParam(hAcceptInput, HookParamType_CBaseEntity);
DHookAddParam(hAcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); //varaint_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20
DHookAddParam(hAcceptInput, HookParamType_Int);
offset = GameConfGetOffset(temp, "GetMaxPlayerSpeed");
hGetSpeed = DHookCreate(offset, HookType_Entity, ReturnType_Float, ThisPointer_CBaseEntity, GetMaxPlayerSpeedPost);
offset = GameConfGetOffset(temp, "GiveAmmo");
hGiveAmmo = DHookCreate(offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity, GiveAmmo);
DHookAddParam(hGiveAmmo, HookParamType_Int);
DHookAddParam(hGiveAmmo, HookParamType_Int);
DHookAddParam(hGiveAmmo, HookParamType_Bool);
offset = GameConfGetOffset(temp, "OnTakeDamage");
hTakeDamage = DHookCreate(offset, HookType_Entity, ReturnType_Int, ThisPointer_CBaseEntity, OnTakeDamage);
DHookAddParam(hTakeDamage, HookParamType_ObjectPtr, -1, DHookPass_ByRef);
DHookAddEntityListener(ListenType_Created, EntityCreated);
//Add client printf hook pThis requires effort
StartPrepSDKCall(SDKCall_Static);
if(!PrepSDKCall_SetFromConf(temp, SDKConf_Signature, "CreateInterface"))
{
SetFailState("Failed to get CreateInterface");
CloseHandle(temp);
}
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer);
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Pointer, VDECODE_FLAG_ALLOWNULL);
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
new String:iface[64];
if(!GameConfGetKeyValue(temp, "EngineInterface", iface, sizeof(iface)))
{
SetFailState("Failed to get engine interface name");
CloseHandle(temp);
}
new Handle:call = EndPrepSDKCall();
new Address:addr = SDKCall(call, iface, 0);
CloseHandle(call);
if(!addr)
{
SetFailState("Failed to get engine ptr");
}
offset = GameConfGetOffset(temp, "ClientPrintf");
hClientPrintf = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, Hook_ClientPrintf);
DHookAddParam(hClientPrintf, HookParamType_Edict);
DHookAddParam(hClientPrintf, HookParamType_CharPtr);
DHookRaw(hClientPrintf, false, addr);
CloseHandle(temp);
}
public MRESReturn:Hook_ClientPrintf(Handle:hParams)
{
new client = DHookGetParam(hParams, 1);
decl String:buffer[1024];
DHookGetParamString(hParams, 2, buffer, sizeof(buffer));
PrintToChat(client, "BUFFER %s", buffer);
return MRES_Ignored;
}
public MRESReturn:AcceptInput(pThis, Handle:hReturn, Handle:hParams)
{
new String:command[128];
DHookGetParamString(hParams, 1, command, sizeof(command));
new type = DHookGetParamObjectPtrVar(hParams, 4, 16,ObjectValueType_Int);
new String:wtf[128];
DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, wtf, sizeof(wtf));
PrintToServer("Command %s Type %i String %s", command, type, wtf);
DHookSetReturn(hReturn, false);
return MRES_Supercede;
}
public OnMapStart()
{
//Hook Gamerules function in map start
DHookGamerules(hCanHaveAmmo, true, RemovalCB);
}
public OnClientPutInServer(client)
{
DHookEntity(hSetModel, false, client, RemovalCB);
DHookEntity(hHookCanUse, true, client, RemovalCB);
DHookEntity(hGetSpeed, true, client, RemovalCB);
DHookEntity(hGiveAmmo, false, client);
DHookEntity(hGetModelName, true, client);
DHookEntity(hTakeDamage, false, client);
DHookEntity(hGetMaxs, true, client, RemovalCB);
DHookEntity(hBloodColor, true, client);
}
public EntityCreated(entity, const String:classname[])
{
if(strcmp(classname, "point_servercommand") == 0)
{
DHookEntity(hAcceptInput, false, entity);
}
}
//int CCSPlayer::OnTakeDamage(CTakeDamageInfo const&)
public MRESReturn:OnTakeDamage(pThis, Handle:hReturn, Handle:hParams)
{
PrintToServer("DHooksHacks = Victim %i, Attacker %i, Inflictor %i, Damage %f", pThis, DHookGetParamObjectPtrVar(hParams, 1, 40, ObjectValueType_Ehandle), DHookGetParamObjectPtrVar(hParams, 1, 36, ObjectValueType_Ehandle), DHookGetParamObjectPtrVar(hParams, 1, 48, ObjectValueType_Float));
if(pThis <= MaxClients && pThis > 0 && !IsFakeClient(pThis))
{
DHookSetParamObjectPtrVar(hParams, 1, 48, ObjectValueType_Float, 0.0);
PrintToChat(pThis, "Pimping your hp");
}
}
// int CBaseCombatCharacter::GiveAmmo(int, int, bool)
public MRESReturn:GiveAmmo(pThis, Handle:hReturn, Handle:hParams)
{
PrintToChat(pThis, "Giving %i of %i supress %i", DHookGetParam(hParams, 1), DHookGetParam(hParams, 2), DHookGetParam(hParams, 3));
return MRES_Ignored;
}
// void CBaseEntity::SetModel(char const*)
public MRESReturn:SetModel(pThis, Handle:hParams)
{
//Change all bot skins to phoenix one
if(IsFakeClient(pThis))
{
DHookSetParamString(hParams, 1, "models/player/t_phoenix.mdl");
return MRES_ChangedHandled;
}
return MRES_Ignored;
}
//float CCSPlayer::GetPlayerMaxSpeed()
public MRESReturn:GetMaxPlayerSpeedPost(pThis, Handle:hReturn)
{
//Make bots slow
if(IsFakeClient(pThis))
{
DHookSetReturn(hReturn, 100.0);
return MRES_Override;
}
return MRES_Ignored;
}
// bool CGameRules::CanHaveAmmo(CBaseCombatCharacter *, int)
public MRESReturn:CanHaveAmmoPost(Handle:hReturn, Handle:hParams)
{
PrintToServer("Can has ammo? %s %i", DHookGetReturn(hReturn)?"true":"false", DHookGetParam(hParams, 2));
return MRES_Ignored;
}
// string_t CBaseEntity::GetModelName(void)
public MRESReturn:GetModelName(pThis, Handle:hReturn)
{
new String:returnval[128];
DHookGetReturnString(hReturn, returnval, sizeof(returnval));
if(IsFakeClient(pThis))
{
PrintToServer("It is a bot, Model should be: models/player/t_phoenix.mdl It is %s", returnval);
}
return MRES_Ignored;
}
// Vector CBasePlayer::GetPlayerMaxs()
public MRESReturn:GetMaxsPost(Handle:hReturn)
{
new Float:vec[3];
DHookGetReturnVector(hReturn, vec);
PrintToServer("Get maxes %.3f, %.3f, %.3f", vec[0], vec[1], vec[2]);
return MRES_Ignored;
}
// bool CBaseCombatCharacter::Weapon_CanUse(CBaseCombatWeapon *)
public MRESReturn:CanUsePost(pThis, Handle:hReturn, Handle:hParams)
{
//Bots get nothing.
if(IsFakeClient(pThis))
{
DHookSetReturn(hReturn, false);
return MRES_Override;
}
return MRES_Ignored;
}
// int CBaseCombatCharacter::BloodColor(void)
public MRESReturn:BloodColorPost(pThis, Handle:hReturn)
{
//Change the bots blood color to goldish yellow
if(IsFakeClient(pThis))
{
DHookSetReturn(hReturn, 2);
return MRES_Supercede;
}
return MRES_Ignored;
}
public RemovalCB(hookid)
{
PrintToServer("Removed hook %i", hookid);
}
Note: Gamerule hooks are removed OnMapEnd so you must hook them again in OnMapStart. Entity hooks are removed when the entity is removed.
Thats all there is to it! :P
Since this is a dev preview crashes can happen. I mostly tested in CS: S Windows. If you experience a crash please post the function, game, OS, and code.
Changelog: - 1.0.1-pre-alpha
- Added Raw hook (allows passing class pointer)
- Fixed certain hooks unhooking randomly.
- 1.0.2-alpha
- Added support for more params (new max is 10)
- Added ability to add entity listeners (To hook OnEntityCreated and OnEntityDestroyed)
- Changed from pre-alpha to alpha
- 1.0.3-alpha
- Fixed various memory leaks.
- Fixed requiring latest gamehelpers i face
- 1.0.4-alpha
- Added support for Object pointer params (CTakeDamage in OnTakeDamage for example)
- Removed the need for a removal callback when hooking (Its not mandatory now).
- 1.0.5-alpha
- Fixed a crash when a hook was removed that had no removal callback.
- 1.0.6-alpha
- Fixed a crash on newer engines.
- 1.0.7-alpha
- Fixed a crash with strings
- Fixed a crash on new engines
- Added support for CS:GO
- Added support for edict_t
- 1.0.8-alpha
- Added support for getting the address of |this|
- 1.0.9-alpha
- Removed asserts from OnEntityCreated
- 1.0.11-alpha
- Made engine specific builds so that it works with tf2 again.
- Hopefully fixed crashes in CS:GO in OnEntityCreated
- Fixed a bug using Edict params.
- Requires SM 1.5 and MM:S 1.9+
- 1.0.12-alpha
- Fixed crashing when accessing NULL pointers
- Added native to check if a param is NULL before accessing it (DHookIsNullParam)
- 1.0.13-alpha
- Fixed a memory leak when unhooking entities.
- Removed the need for gamedata by switching to SDKHooks entity listeners.
- 2.0.0-dev
- Added ability to handle params with a size other than 4
- Restructed how hooking was handled
- Fixed multiple memory leaks
- Use SDKHooks for entity listeners
- Remove the need for SDK specific builds
- Updated to use AMBuild2
- General code rewrite
- Added mac support.
- Automated builds!
- 2.0.1-dev
- Fixed not being able to read or set returns that were pointers.
- Updated test plugin for SM 1.7+
- 2.0.3-dev
- Fix not being able to block void functions.
- Fix not being able to change Vector params.
- 2.0.4-dev
- Fix raw hooks incorrectly comparing and passing incorrect callback functions.
- Fix not being able to change Object Vector params.
- 2.0.5-dev
- Fix using ThrowNativeError outside of natives. (SM 1.8+)
- 2.0.6-dev
- Fixed not being able to set Vector return values.
- 2.1.0-dev
- Fixed a multitude of object param related issues.
- Added new enum values to DHookPassFlag to be used for objects.
- 2.1.1-dev
- Fixed incorrectly unhooking non entity hooks in OnEntityDestroyed
- Fixed not ending context (Thanks BoTox)
- 2.2.0-dev
- Fixed potential crash
- Add support for setting callback when hooking instead of on create only (Allows creating post/pre hooks with one handle)
When installing DHooks2 make sure to remove any previous bin's of DHooks(1)
DHooks2 REQUIRES SourceMod 1.7+
hg93+ REQUIRES SourceMod 1.8+
Credits:
dvander - Helping with asm and being a huge help overall.
KyleS - Input and reviewing my shitty commits.
psychonic - Quacking and helping with buildbot along with coding questions.
asherkin - SourceHook info.
Powerlord - Debugging and fixing crashes.
Links:
Source : https://bitbucket.org/Drifter321/dhooks2
Waterfall : https://builds.alliedmods.net/drifter/waterfall
Builds : http://users.alliedmods.net/~drifter/builds/dhooks/
DHooks with detour support (credit to Peace-Maker)
https://forums.alliedmods.net/showpo...&postcount=589
|