diff --git a/README.md b/README.md index d1f2c40..ffccd0c 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,15 @@ -# Team Fortress 2 Bunnyhop +# Team Fortress 2 Bunnyhopping This is a simple SourceMod plugin that allows players to bunnyhop in Team Fortress 2. -It is different to most other bunnyhop plugins, as it allows you to re-jump before the engine zeroes out -your z-velocity. This makes bunnyhopping feel incedibly smooth, since no speed is lost and no velocity needs to be manually -added by the plugin. +Unlike most other bunnyhop plugins, this implementation makes you jump before the engine zeroes out your z-velocity, preserving 100% of your speed and making each jump feel buttery smooth. ## Features -* Smooth auto-bhop by holding down the jump button +* Smooth auto-bunnyhopping by holding down the jump button * No speed loss on a successful jump * Works well even with high ping - * Allows crouch-bhopping + * Allows jumping while ducked * Unlimited speed while bunnyhopping * Prevents `CTFGameMovement::PreventBunnyJumping` from getting called * Support for multiple jumps in mid-air (e.g. Scout's air dash, halloween spells, etc.) @@ -20,7 +18,6 @@ added by the plugin. ## Dependencies * SourceMod 1.10 -* [DHooks with Detour Support](https://forums.alliedmods.net/showpost.php?p=2588686&postcount=589) * [MemoryPatch](https://github.com/Kenzzer/MemoryPatch) (compile only) ## ConVars diff --git a/addons/sourcemod/gamedata/tf-bhop.txt b/addons/sourcemod/gamedata/tf-bhop.txt index b082c7e..951114c 100644 --- a/addons/sourcemod/gamedata/tf-bhop.txt +++ b/addons/sourcemod/gamedata/tf-bhop.txt @@ -4,17 +4,11 @@ { "Signatures" { - "CTFGameMovement::PreventBunnyJumping" - { - "library" "server" - "linux" "@_ZN15CTFGameMovement19PreventBunnyJumpingEv" - "windows" "\x56\x8B\xF1\x6A\x52\x8B\x8E\xA8\x07\x00\x00\x81\xC1\xB0\x19\x00\x00\xE8\x2A\x2A\x2A\x2A\x84\xC0\x75\x2A" - } "CTFPlayer::CanAirDash" { "library" "server" "linux" "@_ZNK9CTFPlayer10CanAirDashEv" - "windows" "\x2A\x2A\x2A\x2A\x2A\x2A\x57\x8B\xF9\xF7\x87\x84\x1A\x00\x00\x00\x00\x04\x00" + "windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\xF9\xF7\x87\x84\x1A\x00\x00\x00\x00\x04\x00" } "CTFGameMovement::CheckJumpButton" { @@ -22,10 +16,16 @@ "linux" "@_ZN15CTFGameMovement15CheckJumpButtonEv" "windows" "\x55\x8B\xEC\x83\xEC\x0C\x57\x8B\xF9\x8B\x47\x04\x80\xB8\x54\x0A\x00\x00\x00" } + "CTFGameMovement::PreventBunnyJumping" + { + "library" "server" + "linux" "@_ZN15CTFGameMovement19PreventBunnyJumpingEv" + "windows" "\x56\x8B\xF1\x6A\x52\x8B\x8E\xA8\x07\x00\x00\x81\xC1\xB0\x19\x00\x00\xE8\x2A\x2A\x2A\x2A\x84\xC0\x75\x2A" + } } "Addresses" { - "MemoryPatch_AllowDuckJump" + "MemoryPatch_AllowDuckJumping" { "linux" { @@ -38,20 +38,28 @@ "offset" "499" // CTFGameMovement::CheckJumpButton+1F3 } } - } - "Functions" - { - "CTFGameMovement::PreventBunnyJumping" + "MemoryPatch_AllowBunnyJumping" { - "signature" "CTFGameMovement::PreventBunnyJumping" - "callconv" "thiscall" - "return" "void" - "this" "ignore" + "linux" + { + "signature" "CTFGameMovement::PreventBunnyJumping" + "offset" "39" // CTFGameMovement::PreventBunnyJumping+27 + } + "windows" + { + "signature" "CTFGameMovement::PreventBunnyJumping" + "offset" "24" // CTFGameMovement::PreventBunnyJumping+18 + } } } "Keys" { - "MemoryPatch_AllowDuckJump" + "MemoryPatch_AllowDuckJumping" + { + "linux" "\xEB" // jz short -> jmp short + "windows" "\xEB" // jz short -> jmp short + } + "MemoryPatch_AllowBunnyJumping" { "linux" "\xEB" // jz short -> jmp short "windows" "\xEB" // jz short -> jmp short diff --git a/addons/sourcemod/scripting/tf-bhop.sp b/addons/sourcemod/scripting/tf-bhop.sp index 2688b04..4a14bda 100644 --- a/addons/sourcemod/scripting/tf-bhop.sp +++ b/addons/sourcemod/scripting/tf-bhop.sp @@ -16,7 +16,7 @@ */ #include -#include +#include #include #pragma semicolon 1 @@ -35,7 +35,8 @@ ConVar sv_autobunnyhopping; ConVar sv_duckbunnyhopping; Handle g_SDKCallCanAirDash; -MemoryPatch g_MemoryPatchAllowDuckJump; +MemoryPatch g_MemoryPatchAllowDuckJumping; +MemoryPatch g_MemoryPatchAllowBunnyJumping; bool g_InJumpRelease[MAXPLAYERS + 1]; @@ -44,13 +45,14 @@ public Plugin myinfo = name = "Team Fortress 2 Bunnyhop", author = "Mikusch", description = "Simple TF2 bunnyhopping plugin", - version = "1.1.0", + version = "1.2.0", url = "https://github.com/Mikusch/tf-bhop" } public void OnPluginStart() { sv_enablebunnyhopping = CreateConVar("sv_enablebunnyhopping", "1", "Allow player speed to exceed maximum running speed", FCVAR_REPLICATED, true, 0.0, true, 1.0); + sv_enablebunnyhopping.AddChangeHook(ConVarChanged_PreventBunnyJumping); sv_autobunnyhopping = CreateConVar("sv_autobunnyhopping", "1", "Players automatically re-jump while holding jump button", FCVAR_REPLICATED, true, 0.0, true, 1.0); sv_duckbunnyhopping = CreateConVar("sv_duckbunnyhopping", "1", "Allow jumping while ducked", FCVAR_REPLICATED, true, 0.0, true, 1.0); sv_duckbunnyhopping.AddChangeHook(ConVarChanged_DuckBunnyhopping); @@ -59,12 +61,6 @@ public void OnPluginStart() if (gamedata == null) SetFailState("Could not find tf-bhop gamedata"); - DynamicDetour detour = DynamicDetour.FromConf(gamedata, "CTFGameMovement::PreventBunnyJumping"); - if (detour != null) - detour.Enable(Hook_Pre, DHookCallback_PreventBunnyJumpingPre); - else - LogError("Failed to create detour setup handle for function CTFGameMovement::PreventBunnyJumping"); - StartPrepSDKCall(SDKCall_Player); if (PrepSDKCall_SetFromConf(gamedata, SDKConf_Signature, "CTFPlayer::CanAirDash")) { @@ -79,20 +75,19 @@ public void OnPluginStart() } MemoryPatch.SetGameData(gamedata); - - g_MemoryPatchAllowDuckJump = new MemoryPatch("MemoryPatch_AllowDuckJump"); - if (g_MemoryPatchAllowDuckJump != null) - g_MemoryPatchAllowDuckJump.Enable(); - else - LogError("Failed to create memory patch MemoryPatch_AllowDuckJump"); + CreateMemoryPatch(g_MemoryPatchAllowDuckJumping, "MemoryPatch_AllowDuckJumping"); + CreateMemoryPatch(g_MemoryPatchAllowBunnyJumping, "MemoryPatch_AllowBunnyJumping"); delete gamedata; } public void OnPluginEnd() { - if (g_MemoryPatchAllowDuckJump != null) - g_MemoryPatchAllowDuckJump.Disable(); + if (g_MemoryPatchAllowDuckJumping != null) + g_MemoryPatchAllowDuckJumping.Disable(); + + if (g_MemoryPatchAllowBunnyJumping != null) + g_MemoryPatchAllowBunnyJumping.Disable(); } public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) @@ -128,21 +123,33 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 public void ConVarChanged_DuckBunnyhopping(ConVar convar, const char[] oldValue, const char[] newValue) { - if (convar.BoolValue) + if (g_MemoryPatchAllowDuckJumping != null) { - if (g_MemoryPatchAllowDuckJump != null) - g_MemoryPatchAllowDuckJump.Enable(); + if (convar.BoolValue) + g_MemoryPatchAllowDuckJumping.Enable(); + else + g_MemoryPatchAllowDuckJumping.Disable(); } - else +} + +public void ConVarChanged_PreventBunnyJumping(ConVar convar, const char[] oldValue, const char[] newValue) +{ + if (g_MemoryPatchAllowBunnyJumping != null) { - if (g_MemoryPatchAllowDuckJump != null) - g_MemoryPatchAllowDuckJump.Disable(); + if (convar.BoolValue) + g_MemoryPatchAllowBunnyJumping.Enable(); + else + g_MemoryPatchAllowBunnyJumping.Disable(); } } -public MRESReturn DHookCallback_PreventBunnyJumpingPre() +void CreateMemoryPatch(MemoryPatch &handle, const char[] name) { - return sv_enablebunnyhopping.BoolValue ? MRES_Supercede : MRES_Ignored; + handle = new MemoryPatch(name); + if (handle != null) + handle.Enable(); + else + LogError("Failed to create memory patch %s", name); } bool CanAirDash(int client)