Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions addons/sourcemod/gamedata/l4d_vomit_trace_patch.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
"Games"
{
"#default"
{
"Functions"
{
"CBaseAbility::UpdateAbility"
{
"offset" "CBaseAbility::UpdateAbility"
"hooktype" "entity"
"return" "void"
"this" "entity"
}

"CBaseEntity::PhysicsSolidMaskForEntity"
{
"offset" "CBaseEntity::PhysicsSolidMaskForEntity"
"hooktype" "entity"
"return" "int"
"this" "ignore"
}
}
}

"left4dead"
{
"MemPatches"
{
"ShouldHitEntity_MyInfectedPointer"
{
"signature" "CTraceFilterSimpleListNoInfectedOrGhosts_ShouldHitEntity"
"linux"
{
"offset" "101"

/*
* 8B 06 mov eax, [esi]
* 89 34 24 mov [esp+2Ch+var_2C], esi ; IHandleEntity *
*
* OUR_PATCH:
* FF 90 24 01 00 00 call dword ptr [eax+124h] ; CBaseEntity::MyInfectedPointer(void)
* 85 C0 test eax, eax
* 8B 54 24 0C mov edx, [esp+2Ch+var_20]
* 75 CB jnz short loc_2A614C ; return 0 (no collision)
*/
"verify" "\xFF\x90\x2A\x2A\x2A\x2A\x85\xC0\x2A\x2A\x2A\x2A\x75"

"preserve" "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00"

/*
* E8 ? ? ? ? call CBaseEntity::GetTeamNumber(void)
* 83 F8 03 cmp eax, 3
* 8B 54 24 0C mov edx, [esp+2Ch+var_20]
* 74 CB jz short loc_2A614C ; return 0 (no collision)
*/
"patch" "\xE8\x00\x00\x00\x00\x83\xF8\x03\x90\x90\x90\x90\x74"
}
"windows"
{
"offset" "46"

/*
* 8B 06 mov eax, [esi] ; IHandleEntity *
* 8B 90 20 01 00 00 mov edx, [eax+120h] ; CBaseEntity::MyInfectedPointer(void)
* 8B CE mov ecx, esi
* FF D2 call edx
* 85 C0 test eax, eax
* 75 0F jnz short loc_102CCC1D
*/
"verify" "\x8B\x06\x8B\x2A\x2A\x2A\x2A\x2A\x8B\xCE\xFF\x2A\x85\xC0\x75"

/*
* EB 02 jmp short OUR_PATCH
* 66 90 NOP
*
* OUR_PATCH:
* 8B CE mov ecx, esi ; IHandleEntity *
* E8 ? ? ? ? call sub_1003FA90 ; CBaseEntity::GetTeamNumber(void)
* 83 F8 03 cmp eax, 3
* 74 0F jz short loc_102CCC1D
*/
"patch" "\xEB\x02\x66\x90\x8B\xCE\xE8\x00\x00\x00\x00\x83\xF8\x03\x74"
}
}

"OnVomitCollide__TraceRayMask_patch"
{
"signature" "OnVomitCollide"
"linux"
{
"offset" "16Eh"
"verify" "\x0B\x40\x00\x46"
"patch" "\x03\x40\x00\x46"
}
"windows"
{
"offset" "5Ch"
"verify" "\x0B\x40\x00\x46"
"patch" "\x03\x40\x00\x46"
}
}

"OnVomitCollide__ClipRayMask_patch"
{
"signature" "OnVomitCollide"
"linux"
{
"offset" "252h"
"verify" "\x0B\x40\x00\x46"
"patch" "\x03\x40\x00\x46"
}
"windows"
{
"offset" "F1h"
"verify" "\x0B\x40\x00\x46"
"patch" "\x03\x40\x00\x46"
}
}
}

"Offsets"
{
"CBaseAbility::UpdateAbility"
{
"linux" "190"
"windows" "189"
}

"PatchOffset"
{
"linux" "1"
"windows" "7"
}

"CBaseEntity::PhysicsSolidMaskForEntity"
{
"linux" "164"
"windows" "163"
}
}

"Addresses"
{
"CBaseEntity_GetTeamNumber"
{
"linux"
{
"signature" "CBaseEntity_GetTeamNumber"
}
"windows"
{
"signature" "CBaseEntity_GetTeamNumber"
}
}
}

"Signatures"
{
"CTraceFilterSimpleListNoInfectedOrGhosts_ShouldHitEntity"
{
"library" "server"
"linux" "@_ZN40CTraceFilterSimpleListNoInfectedOrGhosts15ShouldHitEntityEP13IHandleEntityi"
"windows" "\x53\x8B\x2A\x2A\x2A\x56\x8B\x2A\x2A\x2A\x57\x53\x56\x8B\x2A\xE8\x2A\x2A\x2A\x2A\x84\x2A\x74"
/* 53 8B ? ? ? 56 8B ? ? ? 57 53 56 8B ? E8 ? ? ? ? 84 ? 74 */
}

"CBaseEntity_GetTeamNumber"
{
"library" "server"
"linux" "@_ZNK11CBaseEntity13GetTeamNumberEv"
"windows" "\x8B\x81\xEC\x01\x00\x00\xC3"
/* 8B 81 EC 01 00 00 C3 */
}

"OnVomitCollide"
{
"library" "server"
"linux" "@_Z14OnVomitCollideP13CTerrorPlayerP11CBaseEntityS2_i6VectorS3_"
"windows" "\x81\xEC\x2A\x2A\x2A\x2A\x8B\x84\x24\x2A\x2A\x2A\x2A\x8B\x8C\x24\x2A\x2A\x2A\x2A\x8B\x94\x24"
/* 81 EC ? ? ? ? 8B 84 24 ? ? ? ? 8B 8C 24 ? ? ? ? 8B 94 24 */
}
}
}

"left4dead2"
{
"Offsets"
{
"CBaseEntity::PhysicsSolidMaskForEntity"
{
"linux" "178"
"windows" "177"
}
}
}
}
181 changes: 181 additions & 0 deletions addons/sourcemod/scripting/l4d_vomit_trace_patch.sp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdkhooks>
#include <dhooks>
#include <sourcescramble>

#define PLUGIN_VERSION "2.5"

public Plugin myinfo =
{
name = "[L4D & 2] Vomit Trace Patch",
author = "Forgetest",
description = "Fix vomit stuck on Infected teammates & allow stricter collision test.",
version = PLUGIN_VERSION,
url = "https://github.com/Target5150/MoYu_Server_Stupid_Plugins"
}

methodmap GameDataWrapper < GameData {
public GameDataWrapper(const char[] file) {
GameData gd = new GameData(file);
if (!gd) SetFailState("Missing gamedata \"%s\"", file);
return view_as<GameDataWrapper>(gd);
}
property GameData Super {
public get() { return view_as<GameData>(this); }
}
public int GetOffset(const char[] key) {
int offset = this.Super.GetOffset(key);
if (offset == -1) SetFailState("Missing offset \"%s\"", key);
return offset;
}
public Address GetAddress(const char[] key) {
Address ptr = this.Super.GetAddress(key);
if (ptr == Address_Null) SetFailState("Missing address \"%s\"", key);
return ptr;
}
public MemoryPatch CreatePatchOrFail(const char[] name, bool enable = false) {
MemoryPatch hPatch = MemoryPatch.CreateFromConf(this, name);
if (!(enable ? hPatch.Enable() : hPatch.Validate()))
SetFailState("Failed to patch \"%s\"", name);
return hPatch;
}
public DynamicHook CreateDHookOrFail(const char[] name) {
DynamicHook hSetup = DynamicHook.FromConf(this, name);
if (!hSetup)
SetFailState("Missing dhook setup \"%s\"", name);
return hSetup;
}
}

#define OP_CALL_SIZE 5

bool g_bLeft4Dead2;

MemoryPatch g_hPatch;
DynamicHook g_hDHook;
int g_iPatchOffs, g_iFuncOffs;

DynamicHook g_hDHook_PhysicsSolidMaskForEntity;

ConVar g_cvStrictCollide;

public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
switch (GetEngineVersion())
{
case Engine_Left4Dead: { g_bLeft4Dead2 = false; }
case Engine_Left4Dead2: { g_bLeft4Dead2 = true; }
default: { strcopy(error, err_max, "Plugin supports L4D & 2 only."); return APLRes_SilentFailure; }
}
return APLRes_Success;
}

public void OnPluginStart()
{
GameDataWrapper conf = new GameDataWrapper("l4d_vomit_trace_patch");

g_hDHook_PhysicsSolidMaskForEntity = conf.CreateDHookOrFail("CBaseEntity::PhysicsSolidMaskForEntity");

if (!g_bLeft4Dead2)
{
g_hPatch = conf.CreatePatchOrFail("ShouldHitEntity_MyInfectedPointer", false);
g_hDHook = conf.CreateDHookOrFail("CBaseAbility::UpdateAbility");

Address pGetTeamNumberFuncAddr = conf.GetAddress("CBaseEntity_GetTeamNumber");
g_iPatchOffs = conf.GetOffset("PatchOffset");
g_iFuncOffs =
view_as<int>(pGetTeamNumberFuncAddr) - (view_as<int>(g_hPatch.Address) + (g_iPatchOffs - 1) + OP_CALL_SIZE);

conf.CreatePatchOrFail("OnVomitCollide__TraceRayMask_patch", true);
conf.CreatePatchOrFail("OnVomitCollide__ClipRayMask_patch", true);
}

delete conf;

g_cvStrictCollide = CreateConVar("vomit_collide_strict", "1", "Stricter vomit collision against hitbox instead of bounding box.", FCVAR_NONE, true, 0.0, true, 1.0);
}

void ApplyPatch(bool patch)
{
static bool patched = false;
if (patch && !patched)
{
if (!g_hPatch.Enable())
SetFailState("Failed to enable patch \"ShouldHitEntity_MyInfectedPointer\"");

StoreToAddress(g_hPatch.Address + view_as<Address>(g_iPatchOffs), g_iFuncOffs, NumberType_Int32);
patched = true;
}
else if (!patch && patched)
{
g_hPatch.Disable();
patched = false;
}
}

public void OnEntityCreated(int entity, const char[] classname)
{
if (classname[0] == 'a')
{
if (!strcmp(classname, "ability_vomit"))
{
if (!g_bLeft4Dead2)
{
g_hDHook.HookEntity(Hook_Pre, entity, CVomit_UpdateAbility);
g_hDHook.HookEntity(Hook_Post, entity, CVomit_UpdateAbility_Post);
}

g_hDHook_PhysicsSolidMaskForEntity.HookEntity(Hook_Post, entity, CVomit_PhysicsSolidMaskForEntity_Post);
}
}
else if (classname[0] == 'v')
{
if (!strcmp(classname, "vomit_particle"))
{
g_hDHook_PhysicsSolidMaskForEntity.HookEntity(Hook_Post, entity, CVomitParticle_PhysicsSolidMaskForEntity_Post);
}
}
}

MRESReturn CVomit_UpdateAbility(int pThis)
{
if (GetEntProp(pThis, Prop_Send, "m_isSpraying"))
{
ApplyPatch(true);
}

return MRES_Ignored;
}

MRESReturn CVomit_UpdateAbility_Post(int pThis)
{
ApplyPatch(false);

return MRES_Ignored;
}

MRESReturn CVomit_PhysicsSolidMaskForEntity_Post(DHookReturn hReturn)
{
// (L4D1) 0x200400B -> MASK_SOLID
// (L4D2) 0x2004003 -> MASK_SOLID & ~CONTENTS_GRATE

int flags = hReturn.Value;

flags &= (~CONTENTS_GRATE); // Unnecessary for L4D2 though

if (g_cvStrictCollide.BoolValue)
{
flags |= CONTENTS_HITBOX;
}

hReturn.Value = flags;
return MRES_Override;
}

MRESReturn CVomitParticle_PhysicsSolidMaskForEntity_Post(DHookReturn hReturn)
{
return CVomit_PhysicsSolidMaskForEntity_Post(hReturn);
}
1 change: 1 addition & 0 deletions cfg/generalfixes.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ sm plugins load fixes/l4d_prop_touching_rules.smx
sm plugins load fixes/l4d2_fix_tank_rock_handoff.smx
sm plugins load fixes/l4d_fix_prop_los.smx
sm plugins load fixes/l4d2_incap_fire_fix.smx
sm plugins load fixes/l4d_vomit_trace_patch.smx

// Anti-Cheat.
sm plugins load optional/l4d2_block_autoaim.smx // Sort of a cheat..?
Expand Down
Loading