From 5d6f25bcc57146ca699ebb01853f23f382c74123 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 17:59:49 +0300 Subject: [PATCH 01/25] Add CreateScriptInstance --- primedev/squirrel/squirrel.cpp | 4 +++- primedev/squirrel/squirrel.h | 17 +++++++++++++++++ primedev/squirrel/squirrelclasstypes.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp index 41a6a782f..1ee0ed27e 100644 --- a/primedev/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -22,7 +22,7 @@ std::shared_ptr getSquirrelLoggerByContext(ScriptContext context) case ScriptContext::UI: return NS::log::SCRIPT_UI; case ScriptContext::CLIENT: - return NS::log::SCRIPT_CL; + return NS::log::SCRIPT_CL;_CalcWeaponMods case ScriptContext::SERVER: return NS::log::SCRIPT_SV; default: @@ -712,6 +712,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module)) g_pSquirrel->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x3E49B0).RCast(); g_pSquirrel->__sq_getentityfrominstance = module.Offset(0x114F0).RCast(); + g_pSquirrel->__sq_createscriptinstance = module.Offset(0xC20E0).RCast(); g_pSquirrel->__sq_GetEntityConstant_CBaseEntity = g_pSquirrel->__sq_GetEntityConstant_CBaseEntity; g_pSquirrel->__sq_getentityfrominstance = g_pSquirrel->__sq_getentityfrominstance; @@ -805,6 +806,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module)) g_pSquirrel->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x418AF0).RCast(); g_pSquirrel->__sq_getentityfrominstance = module.Offset(0x1E920).RCast(); + g_pSquirrel->__sq_createscriptinstance = module.Offset(0x43F2F0).RCast(); g_pSquirrel->logger = NS::log::SCRIPT_SV; // Message buffer stuff diff --git a/primedev/squirrel/squirrel.h b/primedev/squirrel/squirrel.h index 0c1f24d36..cea6ca5d3 100644 --- a/primedev/squirrel/squirrel.h +++ b/primedev/squirrel/squirrel.h @@ -115,6 +115,7 @@ class SquirrelManagerBase sq_getfunctionType __sq_getfunction; sq_getentityfrominstanceType __sq_getentityfrominstance; + sq_createscriptinstanceType __sq_createscriptinstance; sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity; sq_pushnewstructinstanceType __sq_pushnewstructinstance; @@ -399,6 +400,22 @@ template class SquirrelManager : public virtual Squirrel return _call(m_pSQVM->sqvm, (SQInteger)functionVector.size()); } + template SQRESULT Call(SQObject* obj, Args... args) + { + pushobject(m_pSQVM->sqvm, functionobj); // Push the function object + pushroottable(m_pSQVM->sqvm); // Push root table + + FunctionVector functionVector; + SqRecurseArgs(functionVector, args...); + + for (auto& v : functionVector) + { + v(); + } + + return _call(m_pSQVM->sqvm, (SQInteger)functionVector.size()); + } + #pragma endregion public: diff --git a/primedev/squirrel/squirrelclasstypes.h b/primedev/squirrel/squirrelclasstypes.h index 91c3c4683..3a39c957e 100644 --- a/primedev/squirrel/squirrelclasstypes.h +++ b/primedev/squirrel/squirrelclasstypes.h @@ -227,6 +227,7 @@ typedef SQRESULT (*sq_setuserdatatypeidType)(HSquirrelVM* sqvm, SQInteger iStack // sq misc entity funcs typedef void* (*sq_getentityfrominstanceType)(CSquirrelVM* sqvm, SQObject* pInstance, char** ppEntityConstant); +typedef SQObject* (*sq_createscriptinstanceType)(void* ent); typedef char** (*sq_GetEntityConstantType)(); typedef int (*sq_getfunctionType)(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature); From 7b86b5b093dd65bec639b7f8f0eaed5fbc8e1e9a Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 18:04:44 +0300 Subject: [PATCH 02/25] Format Fix --- primedev/squirrel/squirrel.cpp | 2 +- primedev/squirrel/squirrel.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp index 1ee0ed27e..dd5f0f959 100644 --- a/primedev/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -22,7 +22,7 @@ std::shared_ptr getSquirrelLoggerByContext(ScriptContext context) case ScriptContext::UI: return NS::log::SCRIPT_UI; case ScriptContext::CLIENT: - return NS::log::SCRIPT_CL;_CalcWeaponMods + return NS::log::SCRIPT_CL; case ScriptContext::SERVER: return NS::log::SCRIPT_SV; default: diff --git a/primedev/squirrel/squirrel.h b/primedev/squirrel/squirrel.h index cea6ca5d3..1c4b245ff 100644 --- a/primedev/squirrel/squirrel.h +++ b/primedev/squirrel/squirrel.h @@ -404,7 +404,7 @@ template class SquirrelManager : public virtual Squirrel { pushobject(m_pSQVM->sqvm, functionobj); // Push the function object pushroottable(m_pSQVM->sqvm); // Push root table - + FunctionVector functionVector; SqRecurseArgs(functionVector, args...); From ca221ab411459f9bc5b2fd61d7c6b067daa8f240 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 18:07:12 +0300 Subject: [PATCH 03/25] Remove unrelated, not-working code --- primedev/squirrel/squirrel.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/primedev/squirrel/squirrel.h b/primedev/squirrel/squirrel.h index 1c4b245ff..f2d4fd2c9 100644 --- a/primedev/squirrel/squirrel.h +++ b/primedev/squirrel/squirrel.h @@ -400,22 +400,6 @@ template class SquirrelManager : public virtual Squirrel return _call(m_pSQVM->sqvm, (SQInteger)functionVector.size()); } - template SQRESULT Call(SQObject* obj, Args... args) - { - pushobject(m_pSQVM->sqvm, functionobj); // Push the function object - pushroottable(m_pSQVM->sqvm); // Push root table - - FunctionVector functionVector; - SqRecurseArgs(functionVector, args...); - - for (auto& v : functionVector) - { - v(); - } - - return _call(m_pSQVM->sqvm, (SQInteger)functionVector.size()); - } - #pragma endregion public: From 9c40a940586cfb82408316b4bd785d8e09d9ccb4 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 18:20:16 +0300 Subject: [PATCH 04/25] Fix SQMessageBufferPushArg --- primedev/squirrel/squirrel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primedev/squirrel/squirrel.h b/primedev/squirrel/squirrel.h index f2d4fd2c9..a2593947a 100644 --- a/primedev/squirrel/squirrel.h +++ b/primedev/squirrel/squirrel.h @@ -448,7 +448,7 @@ inline VoidFunction SQMessageBufferPushArg(Vector3& arg) { // Vectors template inline VoidFunction SQMessageBufferPushArg(SQObject* arg) { - return [arg]{ g_pSquirrel->pushSQObject(g_pSquirrel->m_pSQVM->sqvm, arg); }; + return [arg]{ g_pSquirrel->pushobject(g_pSquirrel->m_pSQVM->sqvm, arg); }; } // Ints template From ccb86c5dc0f22d347868e2cbec2c42a1c9c4ff94 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Wed, 24 Apr 2024 15:40:08 +0300 Subject: [PATCH 05/25] Add mod weapon vars create an alternative to weapon mods by allowing overrides of any numeric eWeaponVar. --- primedev/Northstar.cmake | 2 + primedev/mods/modweaponvars.cpp | 101 ++++++++++++++++++++++++++++++++ primedev/mods/modweaponvars.h | 22 +++++++ 3 files changed, 125 insertions(+) create mode 100644 primedev/mods/modweaponvars.cpp create mode 100644 primedev/mods/modweaponvars.h diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index aef630c80..0a3dd5383 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -88,6 +88,8 @@ add_library( "mods/modmanager.h" "mods/modsavefiles.cpp" "mods/modsavefiles.h" + "mods/modweaponvars.cpp" + "mods/modweaponvars.h" "plugins/interfaces/interface.h" "plugins/interfaces/interface.cpp" "plugins/interfaces/sys/ISys.h" diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp new file mode 100644 index 000000000..42bcad7fe --- /dev/null +++ b/primedev/mods/modweaponvars.cpp @@ -0,0 +1,101 @@ + +#include "modweaponvars.h" +#include "squirrel/squirrel.h" +#include "client/r2client.h" +#include "server/r2server.h" + +template WeaponVarInfo* weaponVarArray; + +bool fuck = false; + +AUTOHOOK_INIT() + +// what does this function belong to? who knows? +// - it gets called every frame +// - it forces a recalculation of weapon values +// we dont want that, so we dont let it do anything +// we might want to look into this, afaik its a virtual func +AUTOHOOK(Cl_UpdateWeaponVars, client.dll + 0x5B2670, char, __fastcall, (void* a2)) +{ + return '\0'; +} + +ADD_SQFUNC("void", FuckAround, "entity ent, int value, int offset", "god help you", ScriptContext::SERVER | ScriptContext::CLIENT) +{ + char* ent = g_pSquirrel->getentity(sqvm, 1); + int value = g_pSquirrel->getinteger(sqvm, 2); + int offset = g_pSquirrel->getinteger(sqvm, 3); + *(int*)(ent + offset) = value; + return SQRESULT_NULL; +} + +// sidenote, we need a manager for this + transmitting changes from server to client +// ideally use remotefuncs cause this shit gonna get transmitted A LOT!!!! +ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int value", "", ScriptContext::SERVER | ScriptContext::CLIENT) +{ + char* ent = g_pSquirrel->getentity(sqvm, 1); + int weaponVar = g_pSquirrel->getinteger(sqvm, 2); + int value = g_pSquirrel->getinteger(sqvm, 3); + if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive + { + // invalid weapon var index + g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); + return SQRESULT_ERROR; + } + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; + spdlog::info("eWeaponVar type {}", (int)varInfo->type); + if (varInfo->type != WeaponVarType::INTEGER) + { + // invalid type used + g_pSquirrel->raiseerror(sqvm, "weaponVar type is not integer!"); + return SQRESULT_ERROR; + } + if (context == ScriptContext::SERVER) + *(int*)(ent + 0x1410 + varInfo->offset) = value; + else // if (context == ScriptContext::CLIENT) + { + *(int*)(ent + 0x1700 + varInfo->offset) = value; + fuck = true; + } + return SQRESULT_NULL; +} + +ADD_SQFUNC( + "void", ScriptWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT) +{ + char* ent = g_pSquirrel->getentity(sqvm, 1); + int weaponVar = g_pSquirrel->getinteger(sqvm, 2); + float value = g_pSquirrel->getfloat(sqvm, 3); + if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive + { + // invalid weapon var index + g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); + return SQRESULT_ERROR; + } + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; + spdlog::info("eWeaponVar type {}", (int)varInfo->type); + if (varInfo->type != WeaponVarType::FLOAT32) + { + // invalid type used + g_pSquirrel->raiseerror(sqvm, "weaponVar type is not float!"); + return SQRESULT_ERROR; + } + if (context == ScriptContext::SERVER) + *(float*)(ent + 0x1410 + varInfo->offset) = value; + else // if (context == ScriptContext::CLIENT) + *(float*)(ent + 0x1700 + varInfo->offset) = value; + return SQRESULT_NULL; +} + +ON_DLL_LOAD_CLIENT("client.dll", ClWeaponVarArrayAddress, (CModule mod)) +{ + weaponVarArray = (mod.Offset(0x942ca0).RCast()); + AUTOHOOK_DISPATCH(); +} + +ON_DLL_LOAD("server.dll", SvWeaponVarArrayAddress, (CModule mod)) +{ + weaponVarArray = (mod.Offset(0x997dc0).RCast()); +} +// script try { ScriptWeaponVars_SetInt(__w(), eWeaponVar.ammo_clip_size, 50) } catch (ex) {print(ex)} +// script try { ScriptWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, 10) } catch (ex) {print(ex)} diff --git a/primedev/mods/modweaponvars.h b/primedev/mods/modweaponvars.h new file mode 100644 index 000000000..a7a487d8b --- /dev/null +++ b/primedev/mods/modweaponvars.h @@ -0,0 +1,22 @@ +enum WeaponVarType : __int8 +{ + // support for other types isnt really needed + // and would probably allow for some fucking insane + // exploit chain somehow + INTEGER = (char)1, + FLOAT32 = (char)2 +}; + +const int WEAPON_VAR_COUNT = 725; + +#pragma pack(push, 1) +class WeaponVarInfo +{ +public: + const char unk_0[25]; // 0x0 + char type; // 0x19 + int unk_1A; + unsigned short offset; +}; +#pragma pack(pop) +static_assert(sizeof(WeaponVarInfo) == 0x20, "Expected 32, what the fuck???"); From 8316671fac16f7984a81d2dd22b9df5d251e3925 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Wed, 24 Apr 2024 15:49:36 +0300 Subject: [PATCH 06/25] Cleanup code --- primedev/mods/modweaponvars.cpp | 38 +++++++++++++++------------------ primedev/mods/modweaponvars.h | 2 +- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 42bcad7fe..b357ff09a 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -6,31 +6,18 @@ template WeaponVarInfo* weaponVarArray; -bool fuck = false; - AUTOHOOK_INIT() -// what does this function belong to? who knows? -// - it gets called every frame -// - it forces a recalculation of weapon values -// we dont want that, so we dont let it do anything -// we might want to look into this, afaik its a virtual func +// TODO: _actually_ reverse this. +// Seemingly recalculates weapon variables every tick, figure out what this actually does +// rn we are "disabling" this function because it seems to make stuff +// work, figure out why. +// also ""slight"" performance increase AUTOHOOK(Cl_UpdateWeaponVars, client.dll + 0x5B2670, char, __fastcall, (void* a2)) { return '\0'; } -ADD_SQFUNC("void", FuckAround, "entity ent, int value, int offset", "god help you", ScriptContext::SERVER | ScriptContext::CLIENT) -{ - char* ent = g_pSquirrel->getentity(sqvm, 1); - int value = g_pSquirrel->getinteger(sqvm, 2); - int offset = g_pSquirrel->getinteger(sqvm, 3); - *(int*)(ent + offset) = value; - return SQRESULT_NULL; -} - -// sidenote, we need a manager for this + transmitting changes from server to client -// ideally use remotefuncs cause this shit gonna get transmitted A LOT!!!! ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { char* ent = g_pSquirrel->getentity(sqvm, 1); @@ -42,21 +29,24 @@ ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int v g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); return SQRESULT_ERROR; } + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; - spdlog::info("eWeaponVar type {}", (int)varInfo->type); if (varInfo->type != WeaponVarType::INTEGER) { // invalid type used g_pSquirrel->raiseerror(sqvm, "weaponVar type is not integer!"); return SQRESULT_ERROR; } + if (context == ScriptContext::SERVER) + { *(int*)(ent + 0x1410 + varInfo->offset) = value; + } else // if (context == ScriptContext::CLIENT) { *(int*)(ent + 0x1700 + varInfo->offset) = value; - fuck = true; } + return SQRESULT_NULL; } @@ -72,18 +62,24 @@ ADD_SQFUNC( g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); return SQRESULT_ERROR; } + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; - spdlog::info("eWeaponVar type {}", (int)varInfo->type); if (varInfo->type != WeaponVarType::FLOAT32) { // invalid type used g_pSquirrel->raiseerror(sqvm, "weaponVar type is not float!"); return SQRESULT_ERROR; } + if (context == ScriptContext::SERVER) + { *(float*)(ent + 0x1410 + varInfo->offset) = value; + } else // if (context == ScriptContext::CLIENT) + { *(float*)(ent + 0x1700 + varInfo->offset) = value; + } + return SQRESULT_NULL; } diff --git a/primedev/mods/modweaponvars.h b/primedev/mods/modweaponvars.h index a7a487d8b..7098ac2e7 100644 --- a/primedev/mods/modweaponvars.h +++ b/primedev/mods/modweaponvars.h @@ -19,4 +19,4 @@ class WeaponVarInfo unsigned short offset; }; #pragma pack(pop) -static_assert(sizeof(WeaponVarInfo) == 0x20, "Expected 32, what the fuck???"); +static_assert(sizeof(WeaponVarInfo) == 0x20); From 6e445b542c7915ca3c40f654f293b4d620805e6e Mon Sep 17 00:00:00 2001 From: EladNLG Date: Wed, 24 Apr 2024 23:22:06 +0300 Subject: [PATCH 07/25] Replace magic offset with structs --- primedev/Northstar.cmake | 4 ++++ primedev/client/weaponx.cpp | 1 + primedev/client/weaponx.h | 12 ++++++++++++ primedev/mods/modweaponvars.cpp | 14 ++++++++++---- primedev/server/weaponx.cpp | 2 ++ primedev/server/weaponx.h | 14 ++++++++++++++ 6 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 primedev/client/weaponx.cpp create mode 100644 primedev/client/weaponx.h create mode 100644 primedev/server/weaponx.cpp create mode 100644 primedev/server/weaponx.h diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index 0a3dd5383..73201247a 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -26,6 +26,8 @@ add_library( "client/r2client.cpp" "client/r2client.h" "client/rejectconnectionfixes.cpp" + "client/weaponx.h" + "client/weaponx.cpp" "config/profile.cpp" "config/profile.h" "core/convar/concommand.cpp" @@ -134,6 +136,8 @@ add_library( "server/servernethooks.cpp" "server/serverpresence.cpp" "server/serverpresence.h" + "server/weaponx.h" + "server/weaponx.cpp" "shared/exploit_fixes/exploitfixes.cpp" "shared/exploit_fixes/exploitfixes_lzss.cpp" "shared/exploit_fixes/exploitfixes_utf8parser.cpp" diff --git a/primedev/client/weaponx.cpp b/primedev/client/weaponx.cpp new file mode 100644 index 000000000..5e350dd53 --- /dev/null +++ b/primedev/client/weaponx.cpp @@ -0,0 +1 @@ +#include "client/weaponx.h" diff --git a/primedev/client/weaponx.h b/primedev/client/weaponx.h new file mode 100644 index 000000000..259ba8fc1 --- /dev/null +++ b/primedev/client/weaponx.h @@ -0,0 +1,12 @@ +#pragma once + +#pragma pack(push, 1) +struct C_WeaponX +{ +public: + void* vftable; + const char gap_8[5880]; + // is a struct in reality. size may be wrong + const char weaponVars[0x1000]; +}; +#pragma pack(pop) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index b357ff09a..9887d4b06 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -1,4 +1,6 @@ +#include "client/weaponx.h" +#include "server/weaponx.h" #include "modweaponvars.h" #include "squirrel/squirrel.h" #include "client/r2client.h" @@ -40,11 +42,13 @@ ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int v if (context == ScriptContext::SERVER) { - *(int*)(ent + 0x1410 + varInfo->offset) = value; + CWeaponX* weapon = (CWeaponX*)ent; + *(int*)(&weapon->weaponVars[varInfo->offset]) = value; } else // if (context == ScriptContext::CLIENT) { - *(int*)(ent + 0x1700 + varInfo->offset) = value; + C_WeaponX* weapon = (C_WeaponX*)ent; + *(int*)(&weapon->weaponVars[varInfo->offset]) = value; } return SQRESULT_NULL; @@ -73,11 +77,13 @@ ADD_SQFUNC( if (context == ScriptContext::SERVER) { - *(float*)(ent + 0x1410 + varInfo->offset) = value; + CWeaponX* weapon = (CWeaponX*)ent; + *(float*)(&weapon->weaponVars[varInfo->offset]) = value; } else // if (context == ScriptContext::CLIENT) { - *(float*)(ent + 0x1700 + varInfo->offset) = value; + C_WeaponX* weapon = (C_WeaponX*)ent; + *(float*)(&weapon->weaponVars[varInfo->offset]) = value; } return SQRESULT_NULL; diff --git a/primedev/server/weaponx.cpp b/primedev/server/weaponx.cpp new file mode 100644 index 000000000..22a88d457 --- /dev/null +++ b/primedev/server/weaponx.cpp @@ -0,0 +1,2 @@ +#include "client/weaponx.h" +#include "server/weaponx.h" diff --git a/primedev/server/weaponx.h b/primedev/server/weaponx.h new file mode 100644 index 000000000..e4fe372f5 --- /dev/null +++ b/primedev/server/weaponx.h @@ -0,0 +1,14 @@ +#pragma once +#include "client/weaponx.h" + +#pragma pack(push, 1) +struct CWeaponX +{ + void* vftable; + const char gap_8[5128]; + // this is used by the game as the initial offset + // for getting eWeaponVar values. No idea about + // the actual datatype. Start of another struct? + const char weaponVars[0x1000]; +}; +#pragma pack(pop) From b3ae99da444abd2ad2aba9f32ecbada6f3475116 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 01:15:32 +0300 Subject: [PATCH 08/25] Add type checking --- primedev/client/weaponx.h | 2 ++ primedev/mods/modweaponvars.cpp | 33 ++++++++++++++++++++++++++------- primedev/mods/modweaponvars.h | 4 ++-- primedev/server/weaponx.h | 3 +++ 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/primedev/client/weaponx.h b/primedev/client/weaponx.h index 259ba8fc1..18c4634c0 100644 --- a/primedev/client/weaponx.h +++ b/primedev/client/weaponx.h @@ -1,5 +1,7 @@ #pragma once +inline void* C_WeaponX_vftable = nullptr; + #pragma pack(push, 1) struct C_WeaponX { diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 9887d4b06..0df682386 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -8,13 +8,20 @@ template WeaponVarInfo* weaponVarArray; +template bool IsWeapon(void** ent) +{ + if (context == ScriptContext::SERVER) + return *ent == CWeaponX_vftable; + else + return *ent == C_WeaponX_vftable; +} + AUTOHOOK_INIT() -// TODO: _actually_ reverse this. -// Seemingly recalculates weapon variables every tick, figure out what this actually does -// rn we are "disabling" this function because it seems to make stuff -// work, figure out why. -// also ""slight"" performance increase +// TODO: do a code callback after recalculation of mod values +// to reapply script weapon vars +// this is an alternative, but potentially could be bad +// would be way less performant tho - no idea how bad sq performance is AUTOHOOK(Cl_UpdateWeaponVars, client.dll + 0x5B2670, char, __fastcall, (void* a2)) { return '\0'; @@ -22,7 +29,12 @@ AUTOHOOK(Cl_UpdateWeaponVars, client.dll + 0x5B2670, char, __fastcall, (void* a2 ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { - char* ent = g_pSquirrel->getentity(sqvm, 1); + void** ent = g_pSquirrel->getentity(sqvm, 1); + if (!IsWeapon(ent)) + { + g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); + return SQRESULT_ERROR; + } int weaponVar = g_pSquirrel->getinteger(sqvm, 2); int value = g_pSquirrel->getinteger(sqvm, 3); if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive @@ -57,7 +69,12 @@ ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int v ADD_SQFUNC( "void", ScriptWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { - char* ent = g_pSquirrel->getentity(sqvm, 1); + void** ent = g_pSquirrel->getentity(sqvm, 1); + if (!IsWeapon((void**)ent)) + { + g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); + return SQRESULT_ERROR; + } int weaponVar = g_pSquirrel->getinteger(sqvm, 2); float value = g_pSquirrel->getfloat(sqvm, 3); if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive @@ -92,12 +109,14 @@ ADD_SQFUNC( ON_DLL_LOAD_CLIENT("client.dll", ClWeaponVarArrayAddress, (CModule mod)) { weaponVarArray = (mod.Offset(0x942ca0).RCast()); + C_WeaponX_vftable = mod.Offset(0x998638).RCast(); AUTOHOOK_DISPATCH(); } ON_DLL_LOAD("server.dll", SvWeaponVarArrayAddress, (CModule mod)) { weaponVarArray = (mod.Offset(0x997dc0).RCast()); + CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); } // script try { ScriptWeaponVars_SetInt(__w(), eWeaponVar.ammo_clip_size, 50) } catch (ex) {print(ex)} // script try { ScriptWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, 10) } catch (ex) {print(ex)} diff --git a/primedev/mods/modweaponvars.h b/primedev/mods/modweaponvars.h index 7098ac2e7..0cf63276b 100644 --- a/primedev/mods/modweaponvars.h +++ b/primedev/mods/modweaponvars.h @@ -3,8 +3,8 @@ enum WeaponVarType : __int8 // support for other types isnt really needed // and would probably allow for some fucking insane // exploit chain somehow - INTEGER = (char)1, - FLOAT32 = (char)2 + INTEGER = 1, + FLOAT32 = 2 }; const int WEAPON_VAR_COUNT = 725; diff --git a/primedev/server/weaponx.h b/primedev/server/weaponx.h index e4fe372f5..7c8d2c1db 100644 --- a/primedev/server/weaponx.h +++ b/primedev/server/weaponx.h @@ -1,9 +1,12 @@ #pragma once #include "client/weaponx.h" +inline void* CWeaponX_vftable = nullptr; + #pragma pack(push, 1) struct CWeaponX { +public: void* vftable; const char gap_8[5128]; // this is used by the game as the initial offset From defd857e0672a413c9970923227bd2ba63de4d18 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 01:22:13 +0300 Subject: [PATCH 09/25] Remove unnecessary files --- primedev/Northstar.cmake | 2 -- primedev/client/weaponx.cpp | 1 - primedev/server/weaponx.cpp | 2 -- 3 files changed, 5 deletions(-) delete mode 100644 primedev/client/weaponx.cpp delete mode 100644 primedev/server/weaponx.cpp diff --git a/primedev/Northstar.cmake b/primedev/Northstar.cmake index 73201247a..199638648 100644 --- a/primedev/Northstar.cmake +++ b/primedev/Northstar.cmake @@ -27,7 +27,6 @@ add_library( "client/r2client.h" "client/rejectconnectionfixes.cpp" "client/weaponx.h" - "client/weaponx.cpp" "config/profile.cpp" "config/profile.h" "core/convar/concommand.cpp" @@ -137,7 +136,6 @@ add_library( "server/serverpresence.cpp" "server/serverpresence.h" "server/weaponx.h" - "server/weaponx.cpp" "shared/exploit_fixes/exploitfixes.cpp" "shared/exploit_fixes/exploitfixes_lzss.cpp" "shared/exploit_fixes/exploitfixes_utf8parser.cpp" diff --git a/primedev/client/weaponx.cpp b/primedev/client/weaponx.cpp deleted file mode 100644 index 5e350dd53..000000000 --- a/primedev/client/weaponx.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "client/weaponx.h" diff --git a/primedev/server/weaponx.cpp b/primedev/server/weaponx.cpp deleted file mode 100644 index 22a88d457..000000000 --- a/primedev/server/weaponx.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "client/weaponx.h" -#include "server/weaponx.h" From 3647eca5e1d1386048532f67d3307079142f7d01 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 17:57:22 +0300 Subject: [PATCH 10/25] Several Changes (See description) - reformat some error messages - add support for boolean values - expand WeaponVarType enum - set weaponVars length to the correct length - rename functions - replace hook for random func with hooks to Cl_CalcWeaponMods and Sv_CalcWeaponMods --- primedev/client/weaponx.h | 4 +- primedev/mods/modweaponvars.cpp | 98 +++++++++++++++++++++++++++------ primedev/mods/modweaponvars.h | 20 +++++-- primedev/server/weaponx.h | 2 +- 4 files changed, 97 insertions(+), 27 deletions(-) diff --git a/primedev/client/weaponx.h b/primedev/client/weaponx.h index 18c4634c0..34f02cf50 100644 --- a/primedev/client/weaponx.h +++ b/primedev/client/weaponx.h @@ -8,7 +8,7 @@ struct C_WeaponX public: void* vftable; const char gap_8[5880]; - // is a struct in reality. size may be wrong - const char weaponVars[0x1000]; + // is a struct in reality. + const char weaponVars[0xCA0]; }; #pragma pack(pop) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 0df682386..8facf6448 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -7,6 +7,7 @@ #include "server/r2server.h" template WeaponVarInfo* weaponVarArray; +template std::vector weaponModCallbacks; template bool IsWeapon(void** ent) { @@ -18,16 +19,31 @@ template bool IsWeapon(void** ent) AUTOHOOK_INIT() -// TODO: do a code callback after recalculation of mod values -// to reapply script weapon vars -// this is an alternative, but potentially could be bad -// would be way less performant tho - no idea how bad sq performance is -AUTOHOOK(Cl_UpdateWeaponVars, client.dll + 0x5B2670, char, __fastcall, (void* a2)) +AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int unk_0, char* unk_1, char* unk_2, bool unk_3, int unk_4)) { - return '\0'; + bool result = Cl_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4); + + if (IsWeapon((void**)(unk_2 - 0x1700))) + { + // TODO: codecallback + } + + return result; +} + +AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, char* unk_1, char* unk_2, bool unk_3, int unk_4)) +{ + bool result = Sv_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4); + + if (IsWeapon((void**)(unk_2 - 0x1410))) + { + // TODO: codecallback + } + + return result; } -ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int value", "", ScriptContext::SERVER | ScriptContext::CLIENT) +ADD_SQFUNC("void", ModWeaponVars_SetInt, "entity weapon, int weaponVar, int value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); if (!IsWeapon(ent)) @@ -45,10 +61,10 @@ ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int v } WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; - if (varInfo->type != WeaponVarType::INTEGER) + if (varInfo->type != WVT_INTEGER) { // invalid type used - g_pSquirrel->raiseerror(sqvm, "weaponVar type is not integer!"); + g_pSquirrel->raiseerror(sqvm, "eWeaponVar is not an integer!"); return SQRESULT_ERROR; } @@ -67,10 +83,10 @@ ADD_SQFUNC("void", ScriptWeaponVars_SetInt, "entity weapon, int weaponVar, int v } ADD_SQFUNC( - "void", ScriptWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT) + "void", ModWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); - if (!IsWeapon((void**)ent)) + if (!IsWeapon(ent)) { g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); return SQRESULT_ERROR; @@ -85,10 +101,10 @@ ADD_SQFUNC( } WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; - if (varInfo->type != WeaponVarType::FLOAT32) + if (varInfo->type != WVT_FLOAT32) { // invalid type used - g_pSquirrel->raiseerror(sqvm, "weaponVar type is not float!"); + g_pSquirrel->raiseerror(sqvm, "eWeaponVar is not a float!"); return SQRESULT_ERROR; } @@ -105,18 +121,64 @@ ADD_SQFUNC( return SQRESULT_NULL; } +ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool value", "", ScriptContext::SERVER | ScriptContext::CLIENT) +{ + void** ent = g_pSquirrel->getentity(sqvm, 1); + if (!IsWeapon(ent)) + { + g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); + return SQRESULT_ERROR; + } + bool weaponVar = g_pSquirrel->getbool(sqvm, 2); + float value = g_pSquirrel->getfloat(sqvm, 3); + if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive + { + // invalid weapon var index + g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); + return SQRESULT_ERROR; + } + + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; + if (varInfo->type != WVT_BOOLEAN) + { + // invalid type used + g_pSquirrel->raiseerror(sqvm, "eWeaponVar is not a boolean!"); + return SQRESULT_ERROR; + } + + if (context == ScriptContext::SERVER) + { + CWeaponX* weapon = (CWeaponX*)ent; + *(bool*)(&weapon->weaponVars[varInfo->offset]) = value; + } + else // if (context == ScriptContext::CLIENT) + { + C_WeaponX* weapon = (C_WeaponX*)ent; + *(bool*)(&weapon->weaponVars[varInfo->offset]) = value; + } + + return SQRESULT_NULL; +} + +ADD_SQFUNC("void", AddCallback_ApplyModWeaponVars, "void functionref( entity ) callback, int priority", "", ScriptContext::CLIENT | ScriptContext::SERVER) +{ + return SQRESULT_NULL; +} -ON_DLL_LOAD_CLIENT("client.dll", ClWeaponVarArrayAddress, (CModule mod)) +ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) { weaponVarArray = (mod.Offset(0x942ca0).RCast()); C_WeaponX_vftable = mod.Offset(0x998638).RCast(); - AUTOHOOK_DISPATCH(); + AUTOHOOK_DISPATCH_MODULE(client.dll); } -ON_DLL_LOAD("server.dll", SvWeaponVarArrayAddress, (CModule mod)) +ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) { weaponVarArray = (mod.Offset(0x997dc0).RCast()); CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); + AUTOHOOK_DISPATCH_MODULE(server.dll); } -// script try { ScriptWeaponVars_SetInt(__w(), eWeaponVar.ammo_clip_size, 50) } catch (ex) {print(ex)} -// script try { ScriptWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, 10) } catch (ex) {print(ex)} +// script try { ModWeaponVars_SetInt(__w(), eWeaponVar.ammo_clip_size, 50) } catch (ex) {print(ex)} +// script try { ModWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, 10) } catch (ex) {print(ex)} +// script_client foreach (string k, int v in eWeaponVar) try { ModWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, +// __w().GetWeaponSettingFloat(v)) } catch (e){} diff --git a/primedev/mods/modweaponvars.h b/primedev/mods/modweaponvars.h index 0cf63276b..3c5e45758 100644 --- a/primedev/mods/modweaponvars.h +++ b/primedev/mods/modweaponvars.h @@ -1,13 +1,19 @@ enum WeaponVarType : __int8 { - // support for other types isnt really needed - // and would probably allow for some fucking insane - // exploit chain somehow - INTEGER = 1, - FLOAT32 = 2 + WVT_INTEGER = 1, + WVT_FLOAT32 = 2, + WVT_BOOLEAN = 3, + // NOT SUPPORTED ATM // + WVT_STRING = 4, + WVT_ASSET = 5, + WVT_VECTOR = 6 }; -const int WEAPON_VAR_COUNT = 725; +struct WeaponModCallback +{ + int priority; + SQObject func; +}; #pragma pack(push, 1) class WeaponVarInfo @@ -20,3 +26,5 @@ class WeaponVarInfo }; #pragma pack(pop) static_assert(sizeof(WeaponVarInfo) == 0x20); + +const int WEAPON_VAR_COUNT = 725; diff --git a/primedev/server/weaponx.h b/primedev/server/weaponx.h index 7c8d2c1db..3a9adc803 100644 --- a/primedev/server/weaponx.h +++ b/primedev/server/weaponx.h @@ -12,6 +12,6 @@ struct CWeaponX // this is used by the game as the initial offset // for getting eWeaponVar values. No idea about // the actual datatype. Start of another struct? - const char weaponVars[0x1000]; + const char weaponVars[0xCA0]; }; #pragma pack(pop) From 0608df968c53cb147acf588608f15769d0be24d7 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 18:02:47 +0300 Subject: [PATCH 11/25] Format fixes, remove AddCallback_ApplyModWeaponVars we will be doing a script PR instead, as it is easier --- primedev/mods/modweaponvars.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 8facf6448..b4e15b744 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -82,8 +82,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetInt, "entity weapon, int weaponVar, int valu return SQRESULT_NULL; } -ADD_SQFUNC( - "void", ModWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT) +ADD_SQFUNC("void", ModWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); if (!IsWeapon(ent)) @@ -160,11 +159,6 @@ ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool va return SQRESULT_NULL; } -ADD_SQFUNC("void", AddCallback_ApplyModWeaponVars, "void functionref( entity ) callback, int priority", "", ScriptContext::CLIENT | ScriptContext::SERVER) -{ - return SQRESULT_NULL; -} - ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) { weaponVarArray = (mod.Offset(0x942ca0).RCast()); From 86747f0bead30cbbb2fc9a98c4e6329a2c1032b9 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 18:19:55 +0300 Subject: [PATCH 12/25] Add codecallback call --- primedev/mods/modweaponvars.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index b4e15b744..a488d81a0 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -25,7 +25,8 @@ AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int unk_0, if (IsWeapon((void**)(unk_2 - 0x1700))) { - // TODO: codecallback + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1410)); + g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } return result; @@ -37,7 +38,8 @@ AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, if (IsWeapon((void**)(unk_2 - 0x1410))) { - // TODO: codecallback + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1410)); + g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } return result; From 334f5e1ec3f17b7f9a8806780d95463239267cdb Mon Sep 17 00:00:00 2001 From: EladNLG Date: Thu, 25 Apr 2024 19:56:50 +0300 Subject: [PATCH 13/25] Add RecalculateModsForWeapon --- primedev/mods/modweaponvars.cpp | 29 ++++++++++++++++++++++++++--- primedev/server/weaponx.h | 6 ++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index a488d81a0..5c38598b0 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -6,6 +6,9 @@ #include "client/r2client.h" #include "server/r2server.h" +typedef bool (*SetWeaponModBitfield)(CWeaponX* weapon, int mods); + +SetWeaponModBitfield Sv_SetWeaponModBitfield; template WeaponVarInfo* weaponVarArray; template std::vector weaponModCallbacks; @@ -25,7 +28,7 @@ AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int unk_0, if (IsWeapon((void**)(unk_2 - 0x1700))) { - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1410)); + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1700)); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } @@ -161,16 +164,36 @@ ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool va return SQRESULT_NULL; } +// SERVER only because client does this very often +ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext::SERVER) +{ + void** ent = g_pSquirrel->getentity(sqvm, 1); + if (!IsWeapon(ent)) + { + g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); + return SQRESULT_ERROR; + } + CWeaponX* weapon = (CWeaponX*)ent; + if (!Sv_SetWeaponModBitfield(weapon, weapon->currentModBitfield)) + { + g_pSquirrel->raiseerror(sqvm, "Weapon var calculation failed..."); + return SQRESULT_ERROR; + } + + return SQRESULT_NULL; +} + ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) { - weaponVarArray = (mod.Offset(0x942ca0).RCast()); + weaponVarArray = mod.Offset(0x942ca0).RCast(); C_WeaponX_vftable = mod.Offset(0x998638).RCast(); AUTOHOOK_DISPATCH_MODULE(client.dll); } ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) { - weaponVarArray = (mod.Offset(0x997dc0).RCast()); + weaponVarArray = mod.Offset(0x997dc0).RCast(); + Sv_SetWeaponModBitfield = mod.Offset(0x6A63C0).RCast(); CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); AUTOHOOK_DISPATCH_MODULE(server.dll); } diff --git a/primedev/server/weaponx.h b/primedev/server/weaponx.h index 3a9adc803..0274e7301 100644 --- a/primedev/server/weaponx.h +++ b/primedev/server/weaponx.h @@ -8,10 +8,12 @@ struct CWeaponX { public: void* vftable; - const char gap_8[5128]; + const char gap_8[4800]; + int currentModBitfield; // 0x12C8 + const char gap_12C4[324]; // 0x12CC // this is used by the game as the initial offset // for getting eWeaponVar values. No idea about // the actual datatype. Start of another struct? - const char weaponVars[0xCA0]; + const char weaponVars[0xCA0]; // 0x1410 }; #pragma pack(pop) From 24fc718d2af90db9be41da7aa1e7d712355505e7 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Fri, 26 Apr 2024 01:05:50 +0300 Subject: [PATCH 14/25] Add client-side RecalculateModsForWeapon --- primedev/mods/modweaponvars.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 5c38598b0..c25da9030 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -6,9 +6,9 @@ #include "client/r2client.h" #include "server/r2server.h" -typedef bool (*SetWeaponModBitfield)(CWeaponX* weapon, int mods); +typedef bool (*SetWeaponModBitfield)(void* weapon, int mods); -SetWeaponModBitfield Sv_SetWeaponModBitfield; +template SetWeaponModBitfield _SetWeaponModBitfield; template WeaponVarInfo* weaponVarArray; template std::vector weaponModCallbacks; @@ -165,7 +165,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool va } // SERVER only because client does this very often -ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext::SERVER) +ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); if (!IsWeapon(ent)) @@ -174,7 +174,7 @@ ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext: return SQRESULT_ERROR; } CWeaponX* weapon = (CWeaponX*)ent; - if (!Sv_SetWeaponModBitfield(weapon, weapon->currentModBitfield)) + if (!_SetWeaponModBitfield(weapon, weapon->currentModBitfield)) { g_pSquirrel->raiseerror(sqvm, "Weapon var calculation failed..."); return SQRESULT_ERROR; @@ -186,6 +186,8 @@ ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext: ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) { weaponVarArray = mod.Offset(0x942ca0).RCast(); + // requires client prediction, find a way to enforce that(?) + _SetWeaponModBitfield = mod.Offset(0x5B7B80).RCast(); C_WeaponX_vftable = mod.Offset(0x998638).RCast(); AUTOHOOK_DISPATCH_MODULE(client.dll); } @@ -193,7 +195,7 @@ ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) { weaponVarArray = mod.Offset(0x997dc0).RCast(); - Sv_SetWeaponModBitfield = mod.Offset(0x6A63C0).RCast(); + _SetWeaponModBitfield = mod.Offset(0x6A63C0).RCast(); CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); AUTOHOOK_DISPATCH_MODULE(server.dll); } From 8193eb66c3354419cfb97b6803e608a3595f8575 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Fri, 26 Apr 2024 23:58:48 +0300 Subject: [PATCH 15/25] Fix prediction using unholy magic --- primedev/client/weaponx.h | 6 ++- primedev/mods/modweaponvars.cpp | 95 +++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/primedev/client/weaponx.h b/primedev/client/weaponx.h index 34f02cf50..8d337f7c7 100644 --- a/primedev/client/weaponx.h +++ b/primedev/client/weaponx.h @@ -6,8 +6,10 @@ inline void* C_WeaponX_vftable = nullptr; struct C_WeaponX { public: - void* vftable; - const char gap_8[5880]; + void* vftable; + const char gap_8[5552]; + int currentModBitfield; // 0x15B8 + const char gap_15BC[324]; // is a struct in reality. const char weaponVars[0xCA0]; }; diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index c25da9030..81cf96b63 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -6,9 +6,11 @@ #include "client/r2client.h" #include "server/r2server.h" -typedef bool (*SetWeaponModBitfield)(void* weapon, int mods); +typedef bool (*calculateWeaponValuesType)(int bitfield, void* weapon, void* weaponVarLocation); +typedef char* (*get2ndParamForRecalcModFuncType)(void* weapon); -template SetWeaponModBitfield _SetWeaponModBitfield; +template get2ndParamForRecalcModFuncType get2ndParamForRecalcModFunc; +template calculateWeaponValuesType _CalculateWeaponValues; template WeaponVarInfo* weaponVarArray; template std::vector weaponModCallbacks; @@ -22,15 +24,51 @@ template bool IsWeapon(void** ent) AUTOHOOK_INIT() -AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int unk_0, char* unk_1, char* unk_2, bool unk_3, int unk_4)) +// RENAME THIS BEFORE MERGE (REVIEWERS IF YOU STILL SEE THIS BLOCK IT NOWNOWNOWNOWNOW) +AUTOHOOK(Thingy, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * weapon)) { - bool result = Cl_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4); + bool result = Thingy(weapon); - if (IsWeapon((void**)(unk_2 - 0x1700))) + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(weapon); + g_pSquirrel->Call("CodeCallback_PredictWeaponMods", entInstance); + + return result; +} + +// RENAME THIS BEFORE MERGE (REVIEWERS IF YOU STILL SEE THIS BLOCK IT NOWNOWNOWNOWNOW) +AUTOHOOK(Cl_WeaponTick, client.dll + 0x5B48C0, bool, __fastcall, (C_WeaponX * weapon)) +{ + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(weapon); + + bool result = Cl_WeaponTick(weapon); + g_pSquirrel->Call("CodeCallback_PredictWeaponMods", entInstance); + + return result; +} + +// side note, may be an incorrect name +AUTOHOOK(CPlayerSimulate, server.dll + 0x5A6E50, bool, __fastcall, (CBasePlayer * player, int unk_1, bool unk_2)) +{ + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(player); + + g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); + bool result = CPlayerSimulate(player, unk_1, unk_2); + g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); + + return result; +} + +AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int mods, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) +{ + bool result; + if (IsWeapon((void**)(weaponVars - 0x1700))) { - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1700)); + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(weaponVars - 0x1700)); + result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } + else + result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); return result; } @@ -39,7 +77,7 @@ AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, { bool result = Sv_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4); - if (IsWeapon((void**)(unk_2 - 0x1410))) + if (result && IsWeapon((void**)(unk_2 - 0x1410))) { SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1410)); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); @@ -133,8 +171,8 @@ ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool va g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); return SQRESULT_ERROR; } - bool weaponVar = g_pSquirrel->getbool(sqvm, 2); - float value = g_pSquirrel->getfloat(sqvm, 3); + int weaponVar = g_pSquirrel->getinteger(sqvm, 2); + bool value = g_pSquirrel->getbool(sqvm, 3); if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive { // invalid weapon var index @@ -168,34 +206,57 @@ ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool va ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); - if (!IsWeapon(ent)) + if (ent == nullptr) { g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); return SQRESULT_ERROR; } - CWeaponX* weapon = (CWeaponX*)ent; - if (!_SetWeaponModBitfield(weapon, weapon->currentModBitfield)) + if (!IsWeapon(ent)) { - g_pSquirrel->raiseerror(sqvm, "Weapon var calculation failed..."); + g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); return SQRESULT_ERROR; } + if (context == ScriptContext::SERVER) + { + CWeaponX* weapon = (CWeaponX*)ent; + char* secondParamForCalcWeaponValues = get2ndParamForRecalcModFunc(weapon); + if (!_CalculateWeaponValues(weapon->currentModBitfield, secondParamForCalcWeaponValues, (void*)&weapon->weaponVars)) + { + g_pSquirrel->raiseerror(sqvm, "Weapon var calculation failed..."); + return SQRESULT_ERROR; + } + } + else + { + C_WeaponX* weapon = (C_WeaponX*)ent; + char* secondParamForCalcWeaponValues = get2ndParamForRecalcModFunc(weapon); + if (!_CalculateWeaponValues(weapon->currentModBitfield, secondParamForCalcWeaponValues, (void*)&weapon->weaponVars)) + { + g_pSquirrel->raiseerror(sqvm, "Weapon var calculation failed..."); + return SQRESULT_ERROR; + } + } return SQRESULT_NULL; } ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) { - weaponVarArray = mod.Offset(0x942ca0).RCast(); + const ScriptContext context = ScriptContext::CLIENT; + weaponVarArray = mod.Offset(0x942ca0).RCast(); // requires client prediction, find a way to enforce that(?) - _SetWeaponModBitfield = mod.Offset(0x5B7B80).RCast(); + _CalculateWeaponValues = mod.Offset(0x3CA060).RCast(); + get2ndParamForRecalcModFunc = mod.Offset(0xBB4B0).RCast(); C_WeaponX_vftable = mod.Offset(0x998638).RCast(); AUTOHOOK_DISPATCH_MODULE(client.dll); } ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) { - weaponVarArray = mod.Offset(0x997dc0).RCast(); - _SetWeaponModBitfield = mod.Offset(0x6A63C0).RCast(); + const ScriptContext context = ScriptContext::SERVER; + weaponVarArray = mod.Offset(0x997dc0).RCast(); + _CalculateWeaponValues = mod.Offset(0x6C8B30).RCast(); + get2ndParamForRecalcModFunc = mod.Offset(0xF0CD0).RCast(); CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); AUTOHOOK_DISPATCH_MODULE(server.dll); } From 9bc85326c644b967d77f2688c3162a254d3896a7 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sat, 27 Apr 2024 14:15:36 +0300 Subject: [PATCH 16/25] Remove unnecessary hook, rename "thingy" to Cl_WeaponTick i think thats what it is? makes the most sense to me also format fix --- primedev/mods/modweaponvars.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 81cf96b63..d656fa692 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -25,23 +25,12 @@ template bool IsWeapon(void** ent) AUTOHOOK_INIT() // RENAME THIS BEFORE MERGE (REVIEWERS IF YOU STILL SEE THIS BLOCK IT NOWNOWNOWNOWNOW) -AUTOHOOK(Thingy, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * weapon)) +AUTOHOOK(Cl_WeaponTick, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * weapon)) { - bool result = Thingy(weapon); - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(weapon); g_pSquirrel->Call("CodeCallback_PredictWeaponMods", entInstance); - return result; -} - -// RENAME THIS BEFORE MERGE (REVIEWERS IF YOU STILL SEE THIS BLOCK IT NOWNOWNOWNOWNOW) -AUTOHOOK(Cl_WeaponTick, client.dll + 0x5B48C0, bool, __fastcall, (C_WeaponX * weapon)) -{ - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(weapon); - bool result = Cl_WeaponTick(weapon); - g_pSquirrel->Call("CodeCallback_PredictWeaponMods", entInstance); return result; } @@ -256,7 +245,7 @@ ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) const ScriptContext context = ScriptContext::SERVER; weaponVarArray = mod.Offset(0x997dc0).RCast(); _CalculateWeaponValues = mod.Offset(0x6C8B30).RCast(); - get2ndParamForRecalcModFunc = mod.Offset(0xF0CD0).RCast(); + get2ndParamForRecalcModFunc = mod.Offset(0xF0CD0).RCast(); CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); AUTOHOOK_DISPATCH_MODULE(server.dll); } From baf0c5033a738b81ae0541f99d1dfb563a6591fa Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sat, 27 Apr 2024 16:25:14 +0300 Subject: [PATCH 17/25] Format fix --- primedev/client/weaponx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primedev/client/weaponx.h b/primedev/client/weaponx.h index 8d337f7c7..e9f30243a 100644 --- a/primedev/client/weaponx.h +++ b/primedev/client/weaponx.h @@ -6,7 +6,7 @@ inline void* C_WeaponX_vftable = nullptr; struct C_WeaponX { public: - void* vftable; + void* vftable; const char gap_8[5552]; int currentModBitfield; // 0x15B8 const char gap_15BC[324]; From 07fb9f26d03c978b0d1e317427c45794fd3f6f86 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sun, 28 Apr 2024 12:41:54 +0300 Subject: [PATCH 18/25] Add ModWeaponVars_SetString, ModWeaponVars_GetType also change some comments --- primedev/mods/modweaponvars.cpp | 64 +++++++++++++++++++++++++++++++-- primedev/mods/modweaponvars.h | 2 +- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index d656fa692..8aa2d7a60 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -24,7 +24,7 @@ template bool IsWeapon(void** ent) AUTOHOOK_INIT() -// RENAME THIS BEFORE MERGE (REVIEWERS IF YOU STILL SEE THIS BLOCK IT NOWNOWNOWNOWNOW) +// Name might be wrong, we're going off of frequency of calls AUTOHOOK(Cl_WeaponTick, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * weapon)) { SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(weapon); @@ -35,11 +35,13 @@ AUTOHOOK(Cl_WeaponTick, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * we return result; } -// side note, may be an incorrect name +// Name might be wrong, we're going off of frequency of calls AUTOHOOK(CPlayerSimulate, server.dll + 0x5A6E50, bool, __fastcall, (CBasePlayer * player, int unk_1, bool unk_2)) { SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(player); + // we have to do it twice, before AND after, or mispredictions happen. + // no idea why, unfortunately :/ g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); bool result = CPlayerSimulate(player, unk_1, unk_2); g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); @@ -152,6 +154,64 @@ ADD_SQFUNC("void", ModWeaponVars_SetFloat, "entity weapon, int weaponVar, float return SQRESULT_NULL; } + +ADD_SQFUNC("void", ModWeaponVars_GetType, "int eWeaponVar", "", ScriptContext::SERVER | ScriptContext::CLIENT) +{ + int weaponVar = g_pSquirrel->getinteger(sqvm, 1); + + if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive + { + // invalid weapon var index + g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); + return SQRESULT_ERROR; + } + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; + g_pSquirrel->pushinteger(sqvm, varInfo->type); + + return SQRESULT_NOTNULL; +} + +ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, string value", "", ScriptContext::SERVER | ScriptContext::CLIENT) +{ + void** ent = g_pSquirrel->getentity(sqvm, 1); + if (!IsWeapon(ent)) + { + g_pSquirrel->raiseerror(sqvm, "Entity is not a weapon"); + return SQRESULT_ERROR; + } + int weaponVar = g_pSquirrel->getinteger(sqvm, 2); + std::string value = g_pSquirrel->getstring(sqvm, 3); + if (weaponVar < 1 || weaponVar > WEAPON_VAR_COUNT) // weapon vars start at one and end at 725 inclusive + { + // invalid weapon var index + g_pSquirrel->raiseerror(sqvm, "Invalid eWeaponVar!"); + return SQRESULT_ERROR; + } + + WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; + spdlog::info((int)(varInfo->type)); + + if (varInfo->type != WVT_STRING) + { + // invalid type used + g_pSquirrel->raiseerror(sqvm, "eWeaponVar is not a string!"); + return SQRESULT_ERROR; + } + + if (context == ScriptContext::SERVER) + { + CWeaponX* weapon = (CWeaponX*)ent; + *(std::string*)(&weapon->weaponVars[varInfo->offset]) = value; + } + else // if (context == ScriptContext::CLIENT) + { + C_WeaponX* weapon = (C_WeaponX*)ent; + *(std::string*)(&weapon->weaponVars[varInfo->offset]) = value; + } + + return SQRESULT_NULL; +} + ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool value", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); diff --git a/primedev/mods/modweaponvars.h b/primedev/mods/modweaponvars.h index 3c5e45758..45868b3ba 100644 --- a/primedev/mods/modweaponvars.h +++ b/primedev/mods/modweaponvars.h @@ -3,8 +3,8 @@ enum WeaponVarType : __int8 WVT_INTEGER = 1, WVT_FLOAT32 = 2, WVT_BOOLEAN = 3, - // NOT SUPPORTED ATM // WVT_STRING = 4, + // NOT SUPPORTED ATM // WVT_ASSET = 5, WVT_VECTOR = 6 }; From 038d784068deb49ecf643cba8b1cf637455a97b8 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sun, 28 Apr 2024 12:42:38 +0300 Subject: [PATCH 19/25] remove unnecessary comments --- primedev/mods/modweaponvars.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 8aa2d7a60..6831ff640 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -190,7 +190,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, strin WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; spdlog::info((int)(varInfo->type)); - + if (varInfo->type != WVT_STRING) { // invalid type used @@ -309,7 +309,3 @@ ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); AUTOHOOK_DISPATCH_MODULE(server.dll); } -// script try { ModWeaponVars_SetInt(__w(), eWeaponVar.ammo_clip_size, 50) } catch (ex) {print(ex)} -// script try { ModWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, 10) } catch (ex) {print(ex)} -// script_client foreach (string k, int v in eWeaponVar) try { ModWeaponVars_SetFloat(__w(), eWeaponVar.spread_stand_hip, -// __w().GetWeaponSettingFloat(v)) } catch (e){} From 7179a72ec176573061483484db8f83686603088a Mon Sep 17 00:00:00 2001 From: EladNLG Date: Tue, 30 Apr 2024 06:58:34 +0300 Subject: [PATCH 20/25] Replace offsets with `offsetof`, fix GetType returntype --- primedev/mods/modweaponvars.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 6831ff640..f3a1ba35a 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -52,7 +52,7 @@ AUTOHOOK(CPlayerSimulate, server.dll + 0x5A6E50, bool, __fastcall, (CBasePlayer AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int mods, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) { bool result; - if (IsWeapon((void**)(weaponVars - 0x1700))) + if (IsWeapon((void**)(weaponVars - offsetof(C_WeaponX, weaponVars)))) { SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(weaponVars - 0x1700)); result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); @@ -68,7 +68,7 @@ AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, { bool result = Sv_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4); - if (result && IsWeapon((void**)(unk_2 - 0x1410))) + if (result && IsWeapon((void**)(unk_2 - offsetof(CWeaponX, weaponVars)))) { SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1410)); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); @@ -155,7 +155,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetFloat, "entity weapon, int weaponVar, float return SQRESULT_NULL; } -ADD_SQFUNC("void", ModWeaponVars_GetType, "int eWeaponVar", "", ScriptContext::SERVER | ScriptContext::CLIENT) +ADD_SQFUNC("int", ModWeaponVars_GetType, "int eWeaponVar", "", ScriptContext::SERVER | ScriptContext::CLIENT) { int weaponVar = g_pSquirrel->getinteger(sqvm, 1); @@ -293,7 +293,6 @@ ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) { const ScriptContext context = ScriptContext::CLIENT; weaponVarArray = mod.Offset(0x942ca0).RCast(); - // requires client prediction, find a way to enforce that(?) _CalculateWeaponValues = mod.Offset(0x3CA060).RCast(); get2ndParamForRecalcModFunc = mod.Offset(0xBB4B0).RCast(); C_WeaponX_vftable = mod.Offset(0x998638).RCast(); From 7e4f240c3f7635a48f70256947c4611c522045f5 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Fri, 3 May 2024 18:16:23 +0300 Subject: [PATCH 21/25] rename unk_2, replace magic offsets, fix rare crash - the rare crash would happen when the given weaponVars pointer was not from a weapon, and the location before it was not meant to be read --- primedev/mods/modweaponvars.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index f3a1ba35a..741d32747 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -1,4 +1,5 @@ +#include "winbase.h" #include "client/weaponx.h" #include "server/weaponx.h" #include "modweaponvars.h" @@ -52,25 +53,36 @@ AUTOHOOK(CPlayerSimulate, server.dll + 0x5A6E50, bool, __fastcall, (CBasePlayer AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int mods, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) { bool result; + + if (IsBadReadPtr(weaponVars - offsetof(C_WeaponX, weaponVars), 8)) + return Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); + if (IsWeapon((void**)(weaponVars - offsetof(C_WeaponX, weaponVars)))) { - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(weaponVars - 0x1700)); + SQObject* entInstance = + g_pSquirrel->__sq_createscriptinstance((void**)(weaponVars - offsetof(C_WeaponX, weaponVars))); result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } else - result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); + { + return Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); + } return result; } -AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, char* unk_1, char* unk_2, bool unk_3, int unk_4)) +AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) { - bool result = Sv_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4); + bool result = Sv_CalcWeaponMods(unk_0, unk_1, weaponVars, unk_3, unk_4); + + if (IsBadReadPtr(weaponVars - offsetof(CWeaponX, weaponVars), 8)) + return result; - if (result && IsWeapon((void**)(unk_2 - offsetof(CWeaponX, weaponVars)))) + if (result && IsWeapon((void**)(weaponVars - offsetof(CWeaponX, weaponVars)))) { - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(unk_2 - 0x1410)); + SQObject* entInstance = + g_pSquirrel->__sq_createscriptinstance((void**)(weaponVars - offsetof(CWeaponX, weaponVars))); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } @@ -189,7 +201,6 @@ ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, strin } WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; - spdlog::info((int)(varInfo->type)); if (varInfo->type != WVT_STRING) { @@ -201,12 +212,12 @@ ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, strin if (context == ScriptContext::SERVER) { CWeaponX* weapon = (CWeaponX*)ent; - *(std::string*)(&weapon->weaponVars[varInfo->offset]) = value; + *(const char**)(&weapon->weaponVars[varInfo->offset]) = value.c_str(); } else // if (context == ScriptContext::CLIENT) { C_WeaponX* weapon = (C_WeaponX*)ent; - *(std::string*)(&weapon->weaponVars[varInfo->offset]) = value; + *(const char**)(&weapon->weaponVars[varInfo->offset]) = value.c_str(); } return SQRESULT_NULL; From ea7505886e866aa9d5e58965116b277cc639e30e Mon Sep 17 00:00:00 2001 From: EladNLG Date: Fri, 3 May 2024 18:56:06 +0300 Subject: [PATCH 22/25] Fix that crash but for real isbadreadptr sucks actually --- primedev/mods/modweaponvars.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 741d32747..1bee7c992 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -1,5 +1,4 @@ -#include "winbase.h" #include "client/weaponx.h" #include "server/weaponx.h" #include "modweaponvars.h" @@ -50,11 +49,28 @@ AUTOHOOK(CPlayerSimulate, server.dll + 0x5A6E50, bool, __fastcall, (CBasePlayer return result; } +bool IsBadReadPtr(void* p) +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if (::VirtualQuery(p, &mbi, sizeof(mbi))) + { + DWORD mask = + (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY); + bool b = !(mbi.Protect & mask); + // check the page is not a guard page + if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) + b = true; + + return b; + } + return true; +} + AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int mods, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) { bool result; - if (IsBadReadPtr(weaponVars - offsetof(C_WeaponX, weaponVars), 8)) + if (IsBadReadPtr(weaponVars - offsetof(C_WeaponX, weaponVars))) return Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); if (IsWeapon((void**)(weaponVars - offsetof(C_WeaponX, weaponVars)))) @@ -76,7 +92,7 @@ AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, { bool result = Sv_CalcWeaponMods(unk_0, unk_1, weaponVars, unk_3, unk_4); - if (IsBadReadPtr(weaponVars - offsetof(CWeaponX, weaponVars), 8)) + if (IsBadReadPtr(weaponVars - offsetof(CWeaponX, weaponVars))) return result; if (result && IsWeapon((void**)(weaponVars - offsetof(CWeaponX, weaponVars)))) From dcd464c29cdb83cb9d3dcb3d47bbc00b6186a013 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Mon, 6 May 2024 16:41:43 +0300 Subject: [PATCH 23/25] Rename function for consistency --- primedev/mods/modweaponvars.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 1bee7c992..9cc12661b 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -279,7 +279,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool va } // SERVER only because client does this very often -ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext::SERVER | ScriptContext::CLIENT) +ADD_SQFUNC("void", ModWeaponVars_CalculateWeaponMods, "entity weapon", "", ScriptContext::SERVER | ScriptContext::CLIENT) { void** ent = g_pSquirrel->getentity(sqvm, 1); if (ent == nullptr) From bc683ff7636a080a9cc36b59466ec232effa1165 Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sun, 26 May 2024 18:20:39 +0300 Subject: [PATCH 24/25] Make string modweaponvars work, add static asserts --- primedev/client/weaponx.h | 1 + primedev/mods/modweaponvars.cpp | 18 ++++++++++++++---- primedev/mods/modweaponvars.h | 4 ++++ primedev/server/weaponx.h | 1 + primedev/squirrel/squirrel.cpp | 6 ++++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/primedev/client/weaponx.h b/primedev/client/weaponx.h index e9f30243a..7058005b7 100644 --- a/primedev/client/weaponx.h +++ b/primedev/client/weaponx.h @@ -14,3 +14,4 @@ struct C_WeaponX const char weaponVars[0xCA0]; }; #pragma pack(pop) +static_assert(offsetof(C_WeaponX, weaponVars) == 0x1700); diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index 9cc12661b..bb1c022d8 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -6,6 +6,10 @@ #include "client/r2client.h" #include "server/r2server.h" +std::map sv_modWeaponVarStrings; +std::map cl_modWeaponVarStrings; +std::hash hasher; + typedef bool (*calculateWeaponValuesType)(int bitfield, void* weapon, void* weaponVarLocation); typedef char* (*get2ndParamForRecalcModFuncType)(void* weapon); @@ -216,7 +220,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, strin return SQRESULT_ERROR; } - WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; + const WeaponVarInfo* varInfo = &weaponVarArray[weaponVar]; if (varInfo->type != WVT_STRING) { @@ -225,17 +229,23 @@ ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, strin return SQRESULT_ERROR; } + size_t valueHash = hasher(value); if (context == ScriptContext::SERVER) { + if (sv_modWeaponVarStrings.find(valueHash) == sv_modWeaponVarStrings.end()) + sv_modWeaponVarStrings.emplace(valueHash, value); CWeaponX* weapon = (CWeaponX*)ent; - *(const char**)(&weapon->weaponVars[varInfo->offset]) = value.c_str(); + *(const char**)(&weapon->weaponVars[varInfo->offset]) = sv_modWeaponVarStrings[valueHash].c_str(); } else // if (context == ScriptContext::CLIENT) { + if (cl_modWeaponVarStrings.find(valueHash) == cl_modWeaponVarStrings.end()) + cl_modWeaponVarStrings.emplace(valueHash, value); + C_WeaponX* weapon = (C_WeaponX*)ent; - *(const char**)(&weapon->weaponVars[varInfo->offset]) = value.c_str(); + *(const char**)(&weapon->weaponVars[varInfo->offset]) = cl_modWeaponVarStrings[valueHash].c_str(); } - + return SQRESULT_NULL; } diff --git a/primedev/mods/modweaponvars.h b/primedev/mods/modweaponvars.h index 45868b3ba..9faeee315 100644 --- a/primedev/mods/modweaponvars.h +++ b/primedev/mods/modweaponvars.h @@ -1,3 +1,4 @@ +#pragma once enum WeaponVarType : __int8 { WVT_INTEGER = 1, @@ -28,3 +29,6 @@ class WeaponVarInfo static_assert(sizeof(WeaponVarInfo) == 0x20); const int WEAPON_VAR_COUNT = 725; +extern std::map sv_modWeaponVarStrings; +extern std::map cl_modWeaponVarStrings; +extern std::hash hasher; diff --git a/primedev/server/weaponx.h b/primedev/server/weaponx.h index 0274e7301..4120d85b6 100644 --- a/primedev/server/weaponx.h +++ b/primedev/server/weaponx.h @@ -17,3 +17,4 @@ struct CWeaponX const char weaponVars[0xCA0]; // 0x1410 }; #pragma pack(pop) +static_assert(offsetof(CWeaponX, weaponVars) == 0x1410); diff --git a/primedev/squirrel/squirrel.cpp b/primedev/squirrel/squirrel.cpp index dd5f0f959..fe3ddbb76 100644 --- a/primedev/squirrel/squirrel.cpp +++ b/primedev/squirrel/squirrel.cpp @@ -10,6 +10,7 @@ #include "plugins/pluginmanager.h" #include "ns_version.h" #include "core/vanilla.h" +#include "mods/modweaponvars.h" #include @@ -239,6 +240,11 @@ template void SquirrelManager::VMDestroyed() } } + if (context == ScriptContext::SERVER) + sv_modWeaponVarStrings.clear(); + else if (context == ScriptContext::CLIENT) + cl_modWeaponVarStrings.clear(); + g_pPluginManager->InformSqvmDestroying(m_pSQVM); // Discard the previous vm and delete the message buffer. From b6be32c4c0fd12d63b565216c0a4820a668f3dce Mon Sep 17 00:00:00 2001 From: EladNLG Date: Sat, 7 Dec 2024 17:08:53 +0200 Subject: [PATCH 25/25] Use better hooks, make naming consistent --- primedev/mods/modweaponvars.cpp | 62 +++++++++++++++------------------ 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/primedev/mods/modweaponvars.cpp b/primedev/mods/modweaponvars.cpp index bb1c022d8..a9ed9f613 100644 --- a/primedev/mods/modweaponvars.cpp +++ b/primedev/mods/modweaponvars.cpp @@ -28,29 +28,21 @@ template bool IsWeapon(void** ent) AUTOHOOK_INIT() -// Name might be wrong, we're going off of frequency of calls -AUTOHOOK(Cl_WeaponTick, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * weapon)) +// Names might be wrong? Correct if needed +AUTOHOOK(CWeaponX__RegenerateAmmo, server.dll + 0x69E7A0, int, , (CWeaponX * self, CBasePlayer* player, int offhandSlot)) { - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(weapon); - g_pSquirrel->Call("CodeCallback_PredictWeaponMods", entInstance); - - bool result = Cl_WeaponTick(weapon); + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(self); - return result; + g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); + return CWeaponX__RegenerateAmmo(self, player, offhandSlot); } -// Name might be wrong, we're going off of frequency of calls -AUTOHOOK(CPlayerSimulate, server.dll + 0x5A6E50, bool, __fastcall, (CBasePlayer * player, int unk_1, bool unk_2)) +AUTOHOOK(C_WeaponX__RegenerateAmmo, client.dll + 0x5B6020, int, , (C_WeaponX * self, CBasePlayer* player, int offhandSlot)) { - SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(player); + SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance(self); - // we have to do it twice, before AND after, or mispredictions happen. - // no idea why, unfortunately :/ - g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); - bool result = CPlayerSimulate(player, unk_1, unk_2); - g_pSquirrel->Call("CodeCallback_DoWeaponModsForPlayer", entInstance); - - return result; + g_pSquirrel->Call("CodeCallback_PredictWeaponMods", entInstance); + return C_WeaponX__RegenerateAmmo(self, player, offhandSlot); } bool IsBadReadPtr(void* p) @@ -70,31 +62,33 @@ bool IsBadReadPtr(void* p) return true; } -AUTOHOOK(Cl_CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int mods, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) +AUTOHOOK( + C_WeaponX__CalcWeaponMods, client.dll + 0x3CA0B0, bool, __fastcall, (int mods, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) { bool result; if (IsBadReadPtr(weaponVars - offsetof(C_WeaponX, weaponVars))) - return Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); + return C_WeaponX__CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); if (IsWeapon((void**)(weaponVars - offsetof(C_WeaponX, weaponVars)))) { + result = C_WeaponX__CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); SQObject* entInstance = g_pSquirrel->__sq_createscriptinstance((void**)(weaponVars - offsetof(C_WeaponX, weaponVars))); - result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); g_pSquirrel->Call("CodeCallback_ApplyModWeaponVars", entInstance); } else { - return Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); + return C_WeaponX__CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4); } return result; } -AUTOHOOK(Sv_CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) +AUTOHOOK( + CWeaponX__CalcWeaponMods, server.dll + 0x6C8B80, bool, __fastcall, (int unk_0, char* unk_1, char* weaponVars, bool unk_3, int unk_4)) { - bool result = Sv_CalcWeaponMods(unk_0, unk_1, weaponVars, unk_3, unk_4); + bool result = CWeaponX__CalcWeaponMods(unk_0, unk_1, weaponVars, unk_3, unk_4); if (IsBadReadPtr(weaponVars - offsetof(CWeaponX, weaponVars))) return result; @@ -245,7 +239,7 @@ ADD_SQFUNC("void", ModWeaponVars_SetString, "entity weapon, int weaponVar, strin C_WeaponX* weapon = (C_WeaponX*)ent; *(const char**)(&weapon->weaponVars[varInfo->offset]) = cl_modWeaponVarStrings[valueHash].c_str(); } - + return SQRESULT_NULL; } @@ -326,16 +320,6 @@ ADD_SQFUNC("void", ModWeaponVars_CalculateWeaponMods, "entity weapon", "", Scrip return SQRESULT_NULL; } -ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) -{ - const ScriptContext context = ScriptContext::CLIENT; - weaponVarArray = mod.Offset(0x942ca0).RCast(); - _CalculateWeaponValues = mod.Offset(0x3CA060).RCast(); - get2ndParamForRecalcModFunc = mod.Offset(0xBB4B0).RCast(); - C_WeaponX_vftable = mod.Offset(0x998638).RCast(); - AUTOHOOK_DISPATCH_MODULE(client.dll); -} - ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) { const ScriptContext context = ScriptContext::SERVER; @@ -345,3 +329,13 @@ ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod)) CWeaponX_vftable = mod.Offset(0x98E2B8).RCast(); AUTOHOOK_DISPATCH_MODULE(server.dll); } + +ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod)) +{ + const ScriptContext context = ScriptContext::CLIENT; + weaponVarArray = mod.Offset(0x942ca0).RCast(); + _CalculateWeaponValues = mod.Offset(0x3CA060).RCast(); + get2ndParamForRecalcModFunc = mod.Offset(0xBB4B0).RCast(); + C_WeaponX_vftable = mod.Offset(0x998638).RCast(); + AUTOHOOK_DISPATCH_MODULE(client.dll); +}