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
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
sm-version: [ '1.11', '1.12' ]
sm-version: [ '1.12' ]

name: "Build SM ${{ matrix.sm-version }}"
steps:
Expand All @@ -28,10 +28,10 @@ jobs:
# Mac zip just because it's smaller & we don't repack the extensions...
wget https://github.com/ErikMinekus/sm-ripext/releases/download/1.3.1/sm-ripext-1.3.1-mac.zip
unzip sm-ripext-1.3.1-mac.zip "addons/sourcemod/scripting/include/*"
wget https://github.com/clugg/sm-json/archive/refs/tags/v5.0.0.tar.gz
tar --strip-components=1 -xvzf v5.0.0.tar.gz sm-json-5.0.0/addons/sourcemod/scripting/include
wget https://github.com/hermansimensen/eventqueue-fix/archive/refs/tags/1.3.2.tar.gz
tar --strip-components=1 -xvzf 1.3.2.tar.gz -C addons/sourcemod
wget https://github.com/clugg/sm-json/archive/refs/tags/v5.0.1.tar.gz
tar --strip-components=1 -xvzf v5.0.1.tar.gz sm-json-5.0.1/addons/sourcemod/scripting/include
wget https://github.com/hermansimensen/eventqueue-fix/archive/refs/heads/main.tar.gz
tar --strip-components=1 -xvzf main.tar.gz -C addons/sourcemod
rm -rf *.zip *.tar.gz addons/sourcemod/.git* addons/sourcemod/LICENSE

- name: Run compiler
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Includes a records system, map zones (start/end marks etc), bonuses, HUD with us

# Requirements:
* Steam version of Counter-Strike: Source, Counter-Strike: Global Offensive, or Team Fortress 2.
* [Metamod:Source](https://www.sourcemm.net/downloads.php?branch=stable) and [SourceMod](https://www.sourcemod.net/downloads.php?branch=stable) 1.11 or higher.
* [Metamod:Source](https://www.sourcemm.net/downloads.php?branch=stable) and [SourceMod](https://www.sourcemod.net/downloads.php?branch=stable) 1.12 or higher.
* A MySQL database (preferably locally hosted) if your database is likely to grow big, or if you want to use the rankings plugin. MySQL server version of 5.5.5 or above (MariaDB equivalent works too) is required.

# Optional requirements, for the best experience:
Expand Down
148 changes: 110 additions & 38 deletions addons/sourcemod/gamedata/shavit.games.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@
{
"library" "server"
"windows" "@CreateInterface"
"windows64" "@CreateInterface"
"linux" "@CreateInterface"
"linux64" "@CreateInterface"
}

"CreateInterface_Engine"
{
"library" "engine"
"windows" "@CreateInterface"
"windows64" "@CreateInterface"
"linux" "@CreateInterface"
"linux64" "@CreateInterface"
}
}

Expand All @@ -31,13 +35,25 @@
"OS"
{
"windows" "1"
"windows64" "1"
"linux" "2"
"linux64" "2"
}

"X64"
{
"windows" "0"
"windows64" "1"
"linux" "0"
"linux64" "1"
}

"ProcessMovement"
{
"windows" "1"
"windows64" "1"
"linux" "2"
"linux64" "2"
}
}
}
Expand Down Expand Up @@ -151,64 +167,104 @@
{
"Addresses"
{
"m_surfaceFriction"
{
"signature" "CBasePlayer->m_surfaceFriction"
"read" "2" // skip the first 2 bytes
}
}

"Offsets"
{
// https://asherkin.github.io/vtable/
// https://asherkin.github.io/vtable/ (you can drop a .so from your srcds into this)
// search string: "func_pushable" and you can find CBaseTrigger::PassesTriggerFilters / CBaseVPhysicsTrigger::PassesTriggerFilters. Follow references to these functions to find the vtable and then calculate the offset...
"CBaseTrigger::PassesTriggerFilters"
{
"windows" "197"
"linux" "198"
"windows" "203"
"windows64" "203"
"linux" "204"
"linux64" "204"
}
// https://asherkin.github.io/vtable/
// https://asherkin.github.io/vtable/ (you can drop a .so from your srcds into this)
// search string: "start %f %f %f" and then check the function call offsets above it and convert them to vtable offsets (divide by 4 most likely or whatever)
"CCSPlayer::GetPlayerMaxSpeed"
{
"windows" "438"
"linux" "439"
"mac" "439"
"windows" "445"
"windows64" "445"
"linux" "446"
"linux64" "446"
}
// https://asherkin.github.io/vtable/
// https://asherkin.github.io/vtable/ (you can drop a .so from your srcds into this)
// search string: "Invalid counterterrorist spawnpoint" and then look for the first function call in each iteration of the loop
"CGameRules::IsSpawnPointValid"
{
"windows" "76"
"windows64" "77" // yes, same as linux64 (according to vtable site)
"linux" "77"
"mac" "77"
"linux64" "77"
}
// https://asherkin.github.io/vtable/
// https://asherkin.github.io/vtable/ (you can drop a .so from your srcds into this)
// search string: "water" to find CBasePlayer::UpdateStepSound. At the bottom there's a vtable call to ::PlayStepSound. Grab that, divide by 4, subtract 1. Bam, UpdateStepSound...
"CBasePlayer::UpdateStepSound"
{
"windows" "358"
"linux" "359"
"windows" "364"
"windows64" "364"
"linux" "365"
"linux64" "365"
}
// find in CCSGameMovement::CheckForLadders which references CCSPlayer::CanGrabLadder
//
// Find CCSPlayer::CanGrabLadder by searching for 4096.0f, then find the function (CheckForLadders) that references it...
// CanGrabLadder might look like this on Windows (or just use symbols on Linux)
// undefined4 __thiscall CCSPlayer::CanGrabLadder(int param_1_00,float *param_1,float *param_2)
// {
// float10 extraout_ST0;
// float fVar1;
// float fVar2;
//
// (*(code *)**(undefined4 **)(param_1_00 + 0x1790))();
// if ((float10)0 < (float10)*(float *)(param_1_00 + 0x1798) - extraout_ST0) {
// fVar1 = *param_1 - *(float *)(param_1_00 + 0x17a8);
// fVar2 = param_1[1] - *(float *)(param_1_00 + 0x17ac);
// if (fVar2 * fVar2 + fVar1 * fVar1 < 4096.0) {
// return 0;
// }
// if ((((NAN(*(float *)(param_1_00 + 0x179c)) || NAN(*param_2)) !=
// (*(float *)(param_1_00 + 0x179c) == *param_2)) &&
// ((NAN(*(float *)(param_1_00 + 0x17a0)) || NAN(param_2[1])) !=
// (*(float *)(param_1_00 + 0x17a0) == param_2[1]))) &&
// ((NAN(*(float *)(param_1_00 + 0x17a4)) || NAN(param_2[2])) !=
// (*(float *)(param_1_00 + 0x17a4) == param_2[2]))) {
// return 0;
// }
// }
// return 1;
// }
"CCSPlayer::m_lastStandingPos"
{
"windows" "5684"
"linux" "5704" // +20 wow that's easy!
"windows" "6016"
"windows64" "6640"
"linux" "6036" // +20 wow that's easy!
"linux64" "6688" // +48 wow that's easy!
}
// find CCSPlayer::CanGrabLadder via 4096.0f or symbols on linux...
"CCSPlayer::m_ladderSurpressionTimer"
{
"windows" "5700"
"linux" "5720" // +20 wow that's easy!
"windows" "6032"
"windows64" "6668"
"linux" "6052" // +20 wow that's easy!
"linux64" "6716" // +48 wow that's easy!
}
// find CCSPlayer::CanGrabLadder via 4096.0f or symbols on linux...
"CCSPlayer::m_lastLadderNormal"
{
"windows" "5712"
"linux" "5732" // +20 wow that's easy!
"windows" "6044"
"windows64" "6672"
"linux" "6064" // +20 wow that's easy!
"linux64" "6720" // +48 wow that's easy!
}
// find CCSPlayer::CanGrabLadder via 4096.0f or symbols on linux...
"CCSPlayer::m_lastLadderPos"
{
"windows" "5724"
"linux" "5744" // +20 wow that's easy!
"windows" "6056"
"windows64" "6684"
"linux" "6076" // +20 wow that's easy!
"linux64" "6732" // +48 wow that's easy!
}
// TODO
"GetClusterForOrigin"
Expand All @@ -226,53 +282,69 @@

"Signatures"
{
// search string: "ReloadEffect" to find CWeaponCSBase::SendReloadEvents and then DoAnimationEvent is probably the second to last function called there.
// search string: "ReloadEffect" to find CWeaponCSBase::SendReloadEvents and then CCSPlayer::DoAnimationEvent is probably the second to last function called there.
"Player::DoAnimationEvent"
{
"windows" "\x55\x8B\xEC\x83\xEC\x10\x89\x4D\xFC\x83\x7D\x08\x02"
"windows" "\x55\x8B\xEC\x83\xEC\x0C\x89\x4D\x2A\x83\x7D\x2A\x02"
"windows64" "\x44\x89\x44\x24\x2A\x89\x54\x24\x2A\x48\x89\x4C\x24\x2A\x48\x83\xEC\x38\x83\x7C\x24\x2A\x02"
"linux" "@_ZN9CCSPlayer16DoAnimationEventE17PlayerAnimEvent_ti"
"linux64" "@_ZN9CCSPlayer16DoAnimationEventE17PlayerAnimEvent_ti"
}
// search string: "-nobots"
// search string: "-nobots" (and then look for the function that also references "fill" and "match" (or just the function with 0 or 1 parameters...))
"BotManager::MaintainBotQuota"
{
"windows" "\x55\x8B\xEC\x83\xEC\x14\xFF\x15"
"windows64" "\x48\x83\xEC\x78\xFF\x15"
"linux" "@_ZN13CCSBotManager16MaintainBotQuotaEv"
"linux64" "@_ZN13CCSBotManager16MaintainBotQuotaEv"
}
// search string: "Server is hibernating" to find SetHibernating and then go its references
// search string: "Server is hibernating" to find SetHibernating and then go to its references
// NOTE 2025-02-19: Function has been inlined on Windows into SV_Think...
"CGameServer::UpdateHibernationState"
{
"library" "engine"
"windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\xF9\x8B\x07\x8B\x40\x2A\xFF\xD0\x84\xC0\x0F\x84"
"linux" "@_ZN11CGameServer22UpdateHibernationStateEv"
//"windows" ""
"windows64" "\x48\x89\x5C\x24\x2A\x56\x48\x83\xEC\x40\x8B\x05"
"linux" "@_ZN11CGameServer22UpdateHibernationStateEv.part.0"
"linux64" "@_ZN11CGameServer14SetHibernatingEb"
}
// search string: "remove 0x%p: %s-%s" to find PhysicsRemoveToucher.
// Find PhysicsCheckForEntityUntouch by checking the functions that call PhysicsRemoveToucher.
// (should be the function with one argument (this ptr))
"PhysicsCheckForEntityUntouch"
{
"windows" "\x55\x8B\xEC\x83\xEC\x08\x56\x8B\xF1\x8B\x86"
"windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\xF9\x8B\x87"
"windows64" "\x40\x57\x48\x83\xEC\x20\x8B\x81"
"linux" "@_ZN11CBaseEntity28PhysicsCheckForEntityUntouchEv"
"linux64" "@_ZN11CBaseEntity28PhysicsCheckForEntityUntouchEv"
}
// search string: "Could not add bot to the game: Team is full"
// search string: "Could not add bot to the game: Team is full" to find CCSBotManager::BotAddCommand and then follow the if-statement up to find the function call that was full (because TeamFull())
// protip: on csgo we just use mp_randomspawn instead.
"CCSGameRules::TeamFull"
{
"windows" "\x55\x8B\xEC\x56\x8B\xF1\xE8\x2A\x2A\x2A\x2A\x8B\x45\x2A\x83\xE8\x02"
"windows64" "\x48\x89\x5C\x24\x2A\x57\x48\x83\xEC\x20\x8B\xFA\x48\x8B\xD9\xE8\x2A\x2A\x2A\x2A\x83\xEF\x02"
"linux" "@_ZN12CCSGameRules8TeamFullEi"
"linux64" "@_ZN12CCSGameRules8TeamFullEi"
}
// search string: "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n".
// function with one argument is PhysicsRemoveTouchedList
"PhysicsRemoveTouchedList"
{
"windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\x7D\x08\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84"
"windows" "\x55\x8B\xEC\x83\xEC\x08\x53\x8B\x5D\x2A\x8B\x83"
"windows64" "\x40\x55\x56\x48\x83\xEC\x58"
"linux" "@_ZN11CBaseEntity24PhysicsRemoveTouchedListEPS_"
"linux64" "@_ZN11CBaseEntity24PhysicsRemoveTouchedListEPS_"
}
// look for function CGameMovement::CategorizePosition
// and you will see something something *(_DWORD*)(a1[1] + some_offset) = 0x3F800000
// look for CGameMovement::CategorizePosition by searching for 140.0f
// and you will see something something `*(_DWORD*)(a1[1] + some_offset) = 0x3F800000` right at the top
// make a signature at "mov dword ptr[eax+some_offset], 3F800000h"
"CBasePlayer->m_surfaceFriction"
{
"windows" "\xC7\x80\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x8B\x07\xFF\x90"
"linux" "\xC7\x80\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x8B\x03\x89\x1C\x24\xFF\x90\x2A\x2A\x2A\x2A\x8B\x53\x04"
"windows" "\xC7\x80\x2A\x2A\x2A\x2A\x00\x00\x80\x3F\x8B\x07"
"windows64" "\xC7\x80\x2A\x2A\x2A\x2A\x00\x00\x80\x3F\x48\x8B\x01"
"linux" "\xC7\x80\x2A\x2A\x2A\x2A\x00\x00\x80\x3F\x8B\x03"
"linux64" "\xC7\x80\x2A\x2A\x2A\x2A\x00\x00\x80\x3F\x48\x8B\x07"
}
}
}
Expand Down
19 changes: 16 additions & 3 deletions addons/sourcemod/scripting/shavit-core.sp
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,11 @@ public void OnPluginStart()
gB_Protobuf = (GetUserMessageType() == UM_Protobuf);

sv_autobunnyhopping = FindConVar("sv_autobunnyhopping");
if (sv_autobunnyhopping) sv_autobunnyhopping.BoolValue = false;
if (sv_autobunnyhopping)
{
sv_autobunnyhopping.BoolValue = false;
sv_autobunnyhopping.AddChangeHook(OnConVarChanged);
}

if (gEV_Type != Engine_CSGO && gEV_Type != Engine_CSS && gEV_Type != Engine_TF2)
{
Expand Down Expand Up @@ -520,7 +524,7 @@ void LoadDHooks()
SetFailState("Failed to get ProcessMovement offset");
}

Handle processMovement = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, DHook_ProcessMovement);
Handle processMovement = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, DHook_ProcessMovementPre);
DHookAddParam(processMovement, HookParamType_CBaseEntity);
DHookAddParam(processMovement, HookParamType_ObjectPtr);
DHookRaw(processMovement, false, IGameMovement);
Expand Down Expand Up @@ -597,6 +601,13 @@ void LoadDHooks()

public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
if (convar == sv_autobunnyhopping)
{
if (convar.BoolValue)
convar.BoolValue = false;
return;
}

gB_StyleCookies = (newValue[0] != '!');
gI_DefaultStyle = StringToInt(newValue[1]);
}
Expand Down Expand Up @@ -3088,7 +3099,7 @@ public MRESReturn DHook_PreventBunnyJumpingPre()
return MRES_Ignored;
}

public MRESReturn DHook_ProcessMovement(Handle hParams)
public MRESReturn DHook_ProcessMovementPre(Handle hParams)
{
int client = DHookGetParam(hParams, 1);
gI_ClientProcessingMovement = client;
Expand Down Expand Up @@ -3204,6 +3215,8 @@ public MRESReturn DHook_ProcessMovementPost(Handle hParams)
Call_PushCell(time);
Call_Finish();

MaybeDoPhysicsUntouch(client);

return MRES_Ignored;
}

Expand Down
22 changes: 19 additions & 3 deletions addons/sourcemod/scripting/shavit-tas.sp
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,32 @@ public void OnPluginStart()

GameData gamedata = new GameData("shavit.games");

Address surfaceFrctionAddress = gamedata.GetAddress("m_surfaceFriction");
Address surfaceFrictionAddress;

if (surfaceFrctionAddress == Address_Null)
if (gEV_Type == Engine_CSGO)
surfaceFrictionAddress = gamedata.GetAddress("m_surfaceFriction");
else
surfaceFrictionAddress = gamedata.GetMemSig("CBasePlayer->m_surfaceFriction");

if (surfaceFrictionAddress == Address_Null)
{
g_iSurfaceFrictionOffset = -1;
LogError("[XUTAX] The address of m_surfaceFriction is null, defaulting friction values");
}
else
{
g_iSurfaceFrictionOffset = view_as<int>(surfaceFrctionAddress);
if (gEV_Type == Engine_CSGO)
{
g_iSurfaceFrictionOffset = view_as<int>(surfaceFrictionAddress);
}
else
{
int instr = LoadFromAddress(surfaceFrictionAddress, NumberType_Int32);
// The lowest two bytes are the beginning of a `mov`.
// The offset is 100% definitely totally always 16-bit.
// We could just put the offset into the gamedata too but SHUT UP!
g_iSurfaceFrictionOffset = instr >> 16;
}
}

delete gamedata;
Expand Down