Skip to content
Draft
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
3 changes: 2 additions & 1 deletion CS2Fixes.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
<ClInclude Include="src\utils\entity.h" />
<ClInclude Include="src\utils\module.h" />
<ClInclude Include="src\utils\plat.h" />
<ClInclude Include="src\utils\script_function.h" />
<ClInclude Include="src\utils\virtual.h" />
<ClInclude Include="src\cs2_sdk\netmessages.h" />
<ClInclude Include="src\utils\weapon.h" />
Expand All @@ -303,4 +304,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>
5 changes: 4 additions & 1 deletion CS2Fixes.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@
<ClInclude Include="src\utils\virtual.h">
<Filter>Header Files\utils</Filter>
</ClInclude>
<ClInclude Include="src\utils\script_function.h">
<Filter>Header Files\utils</Filter>
</ClInclude>
<ClInclude Include="src\cs2_sdk\schema.h">
<Filter>Header Files\cs2_sdk</Filter>
</ClInclude>
Expand Down Expand Up @@ -428,4 +431,4 @@
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>
42 changes: 0 additions & 42 deletions gamedata/cs2fixes.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,6 @@
"windows": "40 55 53 56 57 41 54 48 8D 6C 24 ? 48 81 EC ? ? ? ? 4D 8B E0",
"linux": "55 66 0F EF C0 48 89 E5 41 57 41 56 41 55 49 89 FD 31 FF"
},
// Should be xref'd right above "flGravity", takes a float arg
"CBaseEntity::SetGravityScale":
{
"library": "server",
"windows": "48 89 5C 24 ? 57 48 83 EC ? F3 0F 10 81 ? ? ? ? 48 8B F9 0F 29 74 24 ? 0F 28 F1 0F 2E C6 7A ? 74 ? BA ? ? ? ? 41 B8 ? ? ? ? 48 81 C1 ? ? ? ? E8 ? ? ? ? 48 8B CF F3 0F 11 B7 ? ? ? ? E8 ? ? ? ? 48 8B 5C 24 ? 0F 28 74 24 ? 48 83 C4 ? 5F C3 CC CC CC CC CC 48 89 5C 24",
"linux": "55 48 89 E5 41 57 41 56 41 55 41 54 53 48 89 FB 48 81 EC ? ? ? ? 0F 2E 87 ? ? ? ? 7A ? 75 ? 48 81 C4 ? ? ? ? 5B 41 5C 41 5D 41 5E 41 5F 5D C3 0F 1F 40 ? 31 C9 BE ? ? ? ? 66 0F EF C9 F3 0F 11 85 ? ? ? ? 48 8D BD ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 0F 29 8D ? ? ? ? 4C 8D A5 ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? 48 C7 85 ? ? ? ? ? ? ? ? C7 85 ? ? ? ? ? ? ? ? 66 89 8D ? ? ? ? E8 ? ? ? ? 48 8B 85 ? ? ? ? 48 8D 15 ? ? ? ? 83 85 ? ? ? ? ? F3 0F 10 85 ? ? ? ? C7 00 ? ? ? ? 48 8B 03 48 8B 80 ? ? ? ? 48 39 D0 0F 85 ? ? ? ? 8B 95 ? ? ? ? 4C 8D 7B ? 85 D2 0F 85 ? ? ? ? 80 BB ? ? ? ? ? 75"
},
// "Game System %s is defined twice!\n"
// Note that this signature points to the instruction with sm_pFirst which is the first qword referenced in the function.
"IGameSystem_InitAllSystems_pFirst":
Expand Down Expand Up @@ -199,30 +192,13 @@
"windows": "48 89 5C 24 10 57 48 83 EC 30 48 8B DA 48 8B F9 48 85 C9",
"linux": "48 85 FF 74 ? 55 48 89 E5 41 55 41 54 49 89 FC"
},
// Look for "SetEntityName", that will be the vscript binding definition
// Scroll a bit down and you'll find something like this (note the offset): *(_QWORD *)(v453 + 64) = sub_1807B0350;
// that function is just a jump to the one we want
"CEntityIdentity_SetEntityName":
{
"library": "server",
"windows": "48 89 5C 24 10 57 48 83 EC 20 48 8B D9 4C 8B C2",
"linux": "55 48 89 F2 48 89 E5 41 55 41 54 53"
},
// "Error - cannot add bots after game is over."
"BotNavIgnore":
{
"library": "server",
"windows": "0F 84 ? ? ? ? 80 B8 ? ? ? ? 00 0F 84 ? ? ? ? 80 3D ? ? ? ? 00 74 15",
"linux": "0F 84 ? ? ? ? 44 0F B6 B8 ? ? ? ? 45 84 FF 0F 84"
},
// next to "soundname", in windows it's the last referenced sub while in linux it's right after
// this is a vscript binding though so it may be removed in the future?
"CBaseEntity_EmitSoundParams":
{
"library": "server",
"windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 48 8B EC 48 81 EC ? ? ? ? 33 C0",
"linux": "48 B8 ? ? ? ? ? ? ? ? 55 0F 28 D0"
},
// "ParticleEffect", found in a function with 9 arguments
"DispatchParticleEffect":
{
Expand Down Expand Up @@ -401,12 +377,6 @@
"windows": 53,
"linux": 52
},
// String: "%s<%i><%s><%s>" ChangeTeam() CTMDBG..."
"CCSPlayerController_ChangeTeam":
{
"windows": 103,
"linux": 102
},
"CBaseEntity::Use":
{
"windows": 145,
Expand All @@ -432,18 +402,6 @@
"windows": 163,
"linux": 162
},
// For these two, look for the names, you'll find vscript bindings
// Scroll down to where the var + 64 gets set to a function, that calls the offset we want
"IsPlayerPawn":
{
"windows": 169,
"linux": 168
},
"IsPlayerController":
{
"windows": 170,
"linux": 169
},
"CollisionRulesChanged":
{
"windows": 186,
Expand Down
26 changes: 23 additions & 3 deletions src/addresses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
return false; \
Message("Found %s at 0x%p\n", name, variable);

#define RESOLVE_SF(className, funcName, variable) \
if (!variable.Initialize(GetScriptFunction(className, funcName))) \
return false; \
if (variable.IsVirtual()) \
Message("Found %s::%s at vtable index %i\n", className, funcName, variable.GetOffset()); \
else \
Message("Found %s::%s at 0x%p\n", className, funcName, variable.GetPtr());

bool addresses::Initialize(CGameConfig* g_GameConfig)
{
modules::engine = new CModule(ROOTBIN, "engine2");
Expand All @@ -51,7 +59,6 @@ bool addresses::Initialize(CGameConfig* g_GameConfig)
#endif

RESOLVE_SIG(g_GameConfig, "SetGroundEntity", addresses::SetGroundEntity);
RESOLVE_SIG(g_GameConfig, "CBaseEntity::SetGravityScale", addresses::SetGravityScale);
RESOLVE_SIG(g_GameConfig, "CCSPlayerController_SwitchTeam", addresses::CCSPlayerController_SwitchTeam);
RESOLVE_SIG(g_GameConfig, "CBasePlayerController_SetPawn", addresses::CBasePlayerController_SetPawn);
RESOLVE_SIG(g_GameConfig, "CBaseModelEntity_SetModel", addresses::CBaseModelEntity_SetModel);
Expand All @@ -62,8 +69,6 @@ bool addresses::Initialize(CGameConfig* g_GameConfig)
RESOLVE_SIG(g_GameConfig, "CGameRules_TerminateRound", addresses::CGameRules_TerminateRound);
RESOLVE_SIG(g_GameConfig, "CreateEntityByName", addresses::CreateEntityByName);
RESOLVE_SIG(g_GameConfig, "DispatchSpawn", addresses::DispatchSpawn);
RESOLVE_SIG(g_GameConfig, "CEntityIdentity_SetEntityName", addresses::CEntityIdentity_SetEntityName);
RESOLVE_SIG(g_GameConfig, "CBaseEntity_EmitSoundParams", addresses::CBaseEntity_EmitSoundParams);
RESOLVE_SIG(g_GameConfig, "DispatchParticleEffect", addresses::DispatchParticleEffect);
RESOLVE_SIG(g_GameConfig, "CBaseEntity_EmitSoundFilter", addresses::CBaseEntity_EmitSoundFilter);
RESOLVE_SIG(g_GameConfig, "CBaseEntity_SetMoveType", addresses::CBaseEntity_SetMoveType);
Expand Down Expand Up @@ -101,3 +106,18 @@ bool addresses::InitializeBanMap(CGameConfig* g_GameConfig)
Message("Found %s at 0x%p\n", "CCSGameRules__sm_mapGcBanInformation", addresses::sm_mapGcBanInformation);
return true;
}

bool addresses::InitializeScriptFunctions()
{
ExecuteOnce
(
RESOLVE_SF("CBaseEntity", "SetGravity", SetGravityScale);
RESOLVE_SF("CBaseEntity", "SetEntityName", ScriptSetEntityName);
RESOLVE_SF("CBaseEntity", "EmitSoundParams", ScriptEmitSoundParams);
RESOLVE_SF("CBaseEntity", "SetTeam", ChangeTeam);
RESOLVE_SF("CBaseEntity", "IsPlayerPawn", IsPlayerPawn);
RESOLVE_SF("CBaseEntity", "IsPlayerController", IsPlayerController);
)

return true;
}
14 changes: 10 additions & 4 deletions src/addresses.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "platform.h"
#include "stdint.h"
#include "utils/module.h"
#include "utils/script_function.h"
#include "utlstring.h"
#include "variant.h"

Expand Down Expand Up @@ -74,11 +75,11 @@ namespace addresses
{
bool Initialize(CGameConfig* g_GameConfig);
bool InitializeBanMap(CGameConfig* g_GameConfig);
bool InitializeScriptFunctions();

inline CUtlMap<uint32, CGcBanInformation_t, uint32>* sm_mapGcBanInformation;

inline void(FASTCALL* SetGroundEntity)(CBaseEntity* ent, CBaseEntity* ground, CBaseEntity* unk3);
inline void(FASTCALL* SetGravityScale)(CBaseEntity*, float);
inline void(FASTCALL* CCSPlayerController_SwitchTeam)(CCSPlayerController* pController, uint32 team);
inline void(FASTCALL* CBasePlayerController_SetPawn)(CBasePlayerController* pController, CCSPlayerPawn* pPawn, bool a3, bool a4, bool a5, bool a6);
inline void(FASTCALL* CBaseModelEntity_SetModel)(CBaseModelEntity* pModel, const char* pszModel);
Expand All @@ -97,8 +98,6 @@ namespace addresses
inline void(FASTCALL* CGameRules_TerminateRound)(CGameRules* pGameRules, float delay, unsigned int reason, int64 a4);
inline CBaseEntity*(FASTCALL* CreateEntityByName)(const char* className, int iForceEdictIndex);
inline void(FASTCALL* DispatchSpawn)(CBaseEntity* pEntity, CEntityKeyValues* pEntityKeyValues);
inline void(FASTCALL* CEntityIdentity_SetEntityName)(CEntityIdentity* pEntity, const char* pName);
inline void(FASTCALL* CBaseEntity_EmitSoundParams)(CBaseEntity* pEntity, const char* pszSound, int nPitch, float flVolume, float flDelay);
inline int(FASTCALL* DispatchParticleEffect)(const char* pszParticleName, int iAttachType, CBaseEntity* pEntity,
char iAttachmentPoint, CUtlSymbolLarge iAttachmentName, bool bResetAllParticlesOnEntity, int nSplitScreenPlayerSlot, IRecipientFilter* a7, byte* a8);
inline StartSoundEventInfo(FASTCALL* CBaseEntity_EmitSoundFilter)(IRecipientFilter& filter, CEntityIndex ent, const EmitSound_t& params);
Expand All @@ -108,4 +107,11 @@ namespace addresses
inline void(FASTCALL* CCSPlayer_WeaponServices_EquipWeapon)(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon);
inline void(FASTCALL* GetSpawnGroups)(CSpawnGroupMgrGameSystem* pSpawnGroupMgr, CUtlVector<SpawnGroupHandle_t>* pList);
inline void(FASTCALL* CBasePlayerPawn_SnapViewAngles)(CBasePlayerPawn* pPawn, QAngle* pAngles);
} // namespace addresses

inline CScriptFunction<void, CBaseEntity, float> SetGravityScale;
inline CScriptFunction<void, CBaseEntity, const char*> ScriptSetEntityName;
inline CScriptFunction<void, CBaseEntity, const char*, int, float, float> ScriptEmitSoundParams;
inline CScriptFunction<void, CBaseEntity, int> ChangeTeam;
inline CScriptFunction<bool, CBaseEntity> IsPlayerPawn;
inline CScriptFunction<bool, CBaseEntity> IsPlayerController;
} // namespace addresses
10 changes: 4 additions & 6 deletions src/cs2_sdk/entity/cbaseentity.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class CBaseEntity : public CEntityInstance

void SetName(const char* pName)
{
addresses::CEntityIdentity_SetEntityName(m_pEntity, pName);
addresses::ScriptSetEntityName(this, pName);
}

void TakeDamage(CTakeDamageInfo& info)
Expand Down Expand Up @@ -196,14 +196,12 @@ class CBaseEntity : public CEntityInstance

bool IsPawn()
{
static int offset = g_GameConfig->GetOffset("IsPlayerPawn");
return CALL_VIRTUAL(bool, offset, this);
return addresses::IsPlayerPawn(this);
}

bool IsController()
{
static int offset = g_GameConfig->GetOffset("IsPlayerController");
return CALL_VIRTUAL(bool, offset, this);
return addresses::IsPlayerController(this);
}

void AcceptInput(const char* pInputName, variant_t value = variant_t(""), CEntityInstance* pActivator = nullptr, CEntityInstance* pCaller = nullptr)
Expand All @@ -226,7 +224,7 @@ class CBaseEntity : public CEntityInstance
// Emit a sound event
void EmitSound(const char* pszSound, int nPitch = 100, float flVolume = 1.0, float flDelay = 0.0)
{
addresses::CBaseEntity_EmitSoundParams(this, pszSound, nPitch, flVolume, flDelay);
addresses::ScriptEmitSoundParams(this, pszSound, nPitch, flVolume, flDelay);
}

StartSoundEventInfo EmitSoundFilter(IRecipientFilter& filter, const char* pszSound, float flVolume = 1.0, float flPitch = 1.0)
Expand Down
3 changes: 1 addition & 2 deletions src/cs2_sdk/entity/ccsplayercontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ class CCSPlayerController : public CBasePlayerController

void ChangeTeam(int iTeam)
{
static int offset = g_GameConfig->GetOffset("CCSPlayerController_ChangeTeam");
CALL_VIRTUAL(void, offset, this, iTeam);
addresses::ChangeTeam(this, iTeam);
}

void SwitchTeam(int iTeam)
Expand Down
6 changes: 6 additions & 0 deletions src/cs2fixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,9 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
g_pEntitySystem = GameEntitySystem();
g_pEntitySystem->AddListenerEntity(g_pEntityListener);

if (!addresses::InitializeScriptFunctions())
Panic("Failed to resolve one or more script functions");

g_playerManager->OnLateLoad();

g_pPanoramaVoteHandler->Reset();
Expand Down Expand Up @@ -614,6 +617,9 @@ void CS2Fixes::Hook_StartupServer(const GameSessionConfiguration_t& config, ISou

RegisterEventListeners();

if (!addresses::InitializeScriptFunctions())
Panic("Failed to resolve one or more script functions");

if (g_bHasTicked)
RemoveTimers(TIMERFLAG_MAP);

Expand Down
106 changes: 106 additions & 0 deletions src/utils/script_function.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* =============================================================================
* CS2Fixes
* Copyright (C) 2023-2026 Source2ZE
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "platform.h"
#include "virtual.h"

template <typename Ret, class Class, typename... Args>
class CScriptFunction
{
public:
inline bool Initialize(void* pFunction)
{
if (!pFunction)
return false;

#ifdef PLATFORM_LINUX
// Itanium ABI: a pointer may encode the virtual index
uintptr_t ptr = reinterpret_cast<uintptr_t>(pFunction);
if (ptr & 1)
{
m_bVirtual = true;
m_iOffset = static_cast<int>((ptr - 1) >> 3);

return true;
}
#endif

m_pFunction = reinterpret_cast<decltype(m_pFunction)>(pFunction);

return true;
}

inline Ret operator()(Class* pThis, Args... args)
{
#ifdef PLATFORM_LINUX
if (m_bVirtual)
{
auto pFunction = vmt::GetVMethod<Ret(FASTCALL*)(Class*, Args...)>(m_iOffset, pThis);

return pFunction(pThis, args...);
}
#endif

return m_pFunction(pThis, args...);
}

inline bool IsVirtual() const
{
#ifdef PLATFORM_LINUX
return m_bVirtual;
#endif

return false;
}

inline void* GetPtr() const
{
#ifdef PLATFORM_LINUX
if (IsVirtual())
return nullptr;
#endif

return reinterpret_cast<void*>(m_pFunction);
}

inline int GetOffset() const
{
#ifdef PLATFORM_LINUX
if (IsVirtual())
return m_iOffset;
#endif

return -1;
}

private:
union
{
Ret(FASTCALL* m_pFunction)(Class*, Args...);
#ifdef PLATFORM_LINUX
int m_iOffset;
#endif
};

#ifdef PLATFORM_LINUX
bool m_bVirtual = false;
#endif
};
Loading
Loading