Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Mod Weapon Vars" #691

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5d6f25b
Add CreateScriptInstance
EladNLG Apr 25, 2024
7b86b5b
Format Fix
EladNLG Apr 25, 2024
ca221ab
Remove unrelated, not-working code
EladNLG Apr 25, 2024
9c40a94
Fix SQMessageBufferPushArg
EladNLG Apr 25, 2024
ccb86c5
Add mod weapon vars
EladNLG Apr 24, 2024
8316671
Cleanup code
EladNLG Apr 24, 2024
6e445b5
Replace magic offset with structs
EladNLG Apr 24, 2024
b3ae99d
Add type checking
EladNLG Apr 24, 2024
defd857
Remove unnecessary files
EladNLG Apr 24, 2024
3647eca
Several Changes (See description)
EladNLG Apr 25, 2024
0608df9
Format fixes, remove AddCallback_ApplyModWeaponVars
EladNLG Apr 25, 2024
86747f0
Add codecallback call
EladNLG Apr 25, 2024
334f5e1
Add RecalculateModsForWeapon
EladNLG Apr 25, 2024
24fc718
Add client-side RecalculateModsForWeapon
EladNLG Apr 25, 2024
8193eb6
Fix prediction using unholy magic
EladNLG Apr 26, 2024
9bc8532
Remove unnecessary hook, rename "thingy" to Cl_WeaponTick
EladNLG Apr 27, 2024
baf0c50
Format fix
EladNLG Apr 27, 2024
07fb9f2
Add ModWeaponVars_SetString, ModWeaponVars_GetType
EladNLG Apr 28, 2024
038d784
remove unnecessary comments
EladNLG Apr 28, 2024
7179a72
Replace offsets with `offsetof`, fix GetType returntype
EladNLG Apr 30, 2024
7e4f240
rename unk_2, replace magic offsets, fix rare crash
EladNLG May 3, 2024
ea75058
Fix that crash but for real
EladNLG May 3, 2024
6dbb6ef
Merge branch 'main' into main
EladNLG May 6, 2024
dcd464c
Rename function for consistency
EladNLG May 6, 2024
bc683ff
Make string modweaponvars work, add static asserts
EladNLG May 26, 2024
485b246
Merge branch 'main' into main
GeckoEidechse Jul 10, 2024
c2d00b1
Merge branch 'main' into main
GeckoEidechse Aug 22, 2024
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
4 changes: 4 additions & 0 deletions primedev/Northstar.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_library(
"client/r2client.cpp"
"client/r2client.h"
"client/rejectconnectionfixes.cpp"
"client/weaponx.h"
"config/profile.cpp"
"config/profile.h"
"core/convar/concommand.cpp"
Expand Down Expand Up @@ -88,6 +89,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"
Expand Down Expand Up @@ -132,6 +135,7 @@ add_library(
"server/servernethooks.cpp"
"server/serverpresence.cpp"
"server/serverpresence.h"
"server/weaponx.h"
"shared/exploit_fixes/exploitfixes.cpp"
"shared/exploit_fixes/exploitfixes_lzss.cpp"
"shared/exploit_fixes/exploitfixes_utf8parser.cpp"
Expand Down
16 changes: 16 additions & 0 deletions primedev/client/weaponx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

inline void* C_WeaponX_vftable = nullptr;

#pragma pack(push, 1)
struct C_WeaponX
{
public:
void* vftable;
const char gap_8[5552];
int currentModBitfield; // 0x15B8
const char gap_15BC[324];
// is a struct in reality.
const char weaponVars[0xCA0];
};
#pragma pack(pop)
255 changes: 255 additions & 0 deletions primedev/mods/modweaponvars.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@

#include "client/weaponx.h"
#include "server/weaponx.h"
#include "modweaponvars.h"
#include "squirrel/squirrel.h"
#include "client/r2client.h"
#include "server/r2server.h"

typedef bool (*calculateWeaponValuesType)(int bitfield, void* weapon, void* weaponVarLocation);
typedef char* (*get2ndParamForRecalcModFuncType)(void* weapon);

template <ScriptContext context> get2ndParamForRecalcModFuncType get2ndParamForRecalcModFunc;
template <ScriptContext context> calculateWeaponValuesType _CalculateWeaponValues;
template <ScriptContext context> WeaponVarInfo* weaponVarArray;
template <ScriptContext context> std::vector<SQObject> weaponModCallbacks;

template <ScriptContext context> bool IsWeapon(void** ent)
{
if (context == ScriptContext::SERVER)
return *ent == CWeaponX_vftable;
else
return *ent == C_WeaponX_vftable;
}

AUTOHOOK_INIT()

// RENAME THIS BEFORE MERGE (REVIEWERS IF YOU STILL SEE THIS BLOCK IT NOWNOWNOWNOWNOW)
AUTOHOOK(Cl_WeaponTick, client.dll + 0x59D1E0, bool, __fastcall, (C_WeaponX * weapon))
EladNLG marked this conversation as resolved.
Show resolved Hide resolved
{
SQObject* entInstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_createscriptinstance(weapon);
g_pSquirrel<ScriptContext::CLIENT>->Call("CodeCallback_PredictWeaponMods", entInstance);

bool result = Cl_WeaponTick(weapon);

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<ScriptContext::SERVER>->__sq_createscriptinstance(player);

g_pSquirrel<ScriptContext::SERVER>->Call("CodeCallback_DoWeaponModsForPlayer", entInstance);
bool result = CPlayerSimulate(player, unk_1, unk_2);
g_pSquirrel<ScriptContext::SERVER>->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<ScriptContext::CLIENT>((void**)(weaponVars - 0x1700)))
{
SQObject* entInstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_createscriptinstance((void**)(weaponVars - 0x1700));
result = Cl_CalcWeaponMods(mods, unk_1, weaponVars, unk_3, unk_4);
g_pSquirrel<ScriptContext::CLIENT>->Call("CodeCallback_ApplyModWeaponVars", entInstance);
}
else
result = 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))
{
bool result = Sv_CalcWeaponMods(unk_0, unk_1, unk_2, unk_3, unk_4);

if (result && IsWeapon<ScriptContext::SERVER>((void**)(unk_2 - 0x1410)))
{
SQObject* entInstance = g_pSquirrel<ScriptContext::SERVER>->__sq_createscriptinstance((void**)(unk_2 - 0x1410));
EladNLG marked this conversation as resolved.
Show resolved Hide resolved
g_pSquirrel<ScriptContext::SERVER>->Call("CodeCallback_ApplyModWeaponVars", entInstance);
}

return result;
}

ADD_SQFUNC("void", ModWeaponVars_SetInt, "entity weapon, int weaponVar, int value", "", ScriptContext::SERVER | ScriptContext::CLIENT)
{
void** ent = g_pSquirrel<context>->getentity<void*>(sqvm, 1);
if (!IsWeapon<context>(ent))
{
g_pSquirrel<context>->raiseerror(sqvm, "Entity is not a weapon");
return SQRESULT_ERROR;
}
int weaponVar = g_pSquirrel<context>->getinteger(sqvm, 2);
int value = g_pSquirrel<context>->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<context>->raiseerror(sqvm, "Invalid eWeaponVar!");
return SQRESULT_ERROR;
}

WeaponVarInfo* varInfo = &weaponVarArray<context>[weaponVar];
if (varInfo->type != WVT_INTEGER)
{
// invalid type used
g_pSquirrel<context>->raiseerror(sqvm, "eWeaponVar is not an integer!");
return SQRESULT_ERROR;
}

if (context == ScriptContext::SERVER)
{
CWeaponX* weapon = (CWeaponX*)ent;
*(int*)(&weapon->weaponVars[varInfo->offset]) = value;
}
else // if (context == ScriptContext::CLIENT)
{
C_WeaponX* weapon = (C_WeaponX*)ent;
*(int*)(&weapon->weaponVars[varInfo->offset]) = value;
}

return SQRESULT_NULL;
}

ADD_SQFUNC("void", ModWeaponVars_SetFloat, "entity weapon, int weaponVar, float value", "", ScriptContext::SERVER | ScriptContext::CLIENT)
{
void** ent = g_pSquirrel<context>->getentity<void*>(sqvm, 1);
if (!IsWeapon<context>(ent))
{
g_pSquirrel<context>->raiseerror(sqvm, "Entity is not a weapon");
return SQRESULT_ERROR;
}
int weaponVar = g_pSquirrel<context>->getinteger(sqvm, 2);
float value = g_pSquirrel<context>->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<context>->raiseerror(sqvm, "Invalid eWeaponVar!");
return SQRESULT_ERROR;
}

WeaponVarInfo* varInfo = &weaponVarArray<context>[weaponVar];
if (varInfo->type != WVT_FLOAT32)
{
// invalid type used
g_pSquirrel<context>->raiseerror(sqvm, "eWeaponVar is not a float!");
return SQRESULT_ERROR;
}

if (context == ScriptContext::SERVER)
{
CWeaponX* weapon = (CWeaponX*)ent;
*(float*)(&weapon->weaponVars[varInfo->offset]) = value;
EladNLG marked this conversation as resolved.
Show resolved Hide resolved
}
else // if (context == ScriptContext::CLIENT)
{
C_WeaponX* weapon = (C_WeaponX*)ent;
*(float*)(&weapon->weaponVars[varInfo->offset]) = value;
EladNLG marked this conversation as resolved.
Show resolved Hide resolved
}

return SQRESULT_NULL;
}
ADD_SQFUNC("void", ModWeaponVars_SetBool, "entity weapon, int weaponVar, bool value", "", ScriptContext::SERVER | ScriptContext::CLIENT)
{
void** ent = g_pSquirrel<context>->getentity<void*>(sqvm, 1);
if (!IsWeapon<context>(ent))
{
g_pSquirrel<context>->raiseerror(sqvm, "Entity is not a weapon");
return SQRESULT_ERROR;
}
int weaponVar = g_pSquirrel<context>->getinteger(sqvm, 2);
bool value = g_pSquirrel<context>->getbool(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<context>->raiseerror(sqvm, "Invalid eWeaponVar!");
return SQRESULT_ERROR;
}

WeaponVarInfo* varInfo = &weaponVarArray<context>[weaponVar];
if (varInfo->type != WVT_BOOLEAN)
{
// invalid type used
g_pSquirrel<context>->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;
}

// SERVER only because client does this very often
ADD_SQFUNC("void", RecalculateModsForWeapon, "entity weapon", "", ScriptContext::SERVER | ScriptContext::CLIENT)
{
void** ent = g_pSquirrel<context>->getentity<void*>(sqvm, 1);
if (ent == nullptr)
{
g_pSquirrel<context>->raiseerror(sqvm, "Entity is not a weapon");
return SQRESULT_ERROR;
}
if (!IsWeapon<context>(ent))
{
g_pSquirrel<context>->raiseerror(sqvm, "Entity is not a weapon");
return SQRESULT_ERROR;
}
if (context == ScriptContext::SERVER)
{
CWeaponX* weapon = (CWeaponX*)ent;
char* secondParamForCalcWeaponValues = get2ndParamForRecalcModFunc<context>(weapon);
if (!_CalculateWeaponValues<context>(weapon->currentModBitfield, secondParamForCalcWeaponValues, (void*)&weapon->weaponVars))
{
g_pSquirrel<context>->raiseerror(sqvm, "Weapon var calculation failed...");
return SQRESULT_ERROR;
}
}
else
{
C_WeaponX* weapon = (C_WeaponX*)ent;
char* secondParamForCalcWeaponValues = get2ndParamForRecalcModFunc<context>(weapon);
if (!_CalculateWeaponValues<context>(weapon->currentModBitfield, secondParamForCalcWeaponValues, (void*)&weapon->weaponVars))
{
g_pSquirrel<context>->raiseerror(sqvm, "Weapon var calculation failed...");
return SQRESULT_ERROR;
}
}

return SQRESULT_NULL;
}

ON_DLL_LOAD_CLIENT("client.dll", ModWeaponVars_ClientInit, (CModule mod))
{
const ScriptContext context = ScriptContext::CLIENT;
weaponVarArray<context> = mod.Offset(0x942ca0).RCast<WeaponVarInfo*>();
// requires client prediction, find a way to enforce that(?)
_CalculateWeaponValues<context> = mod.Offset(0x3CA060).RCast<calculateWeaponValuesType>();
get2ndParamForRecalcModFunc<context> = mod.Offset(0xBB4B0).RCast<get2ndParamForRecalcModFuncType>();
C_WeaponX_vftable = mod.Offset(0x998638).RCast<void*>();
AUTOHOOK_DISPATCH_MODULE(client.dll);
}

ON_DLL_LOAD("server.dll", ModWeaponVars_ServerInit, (CModule mod))
{
const ScriptContext context = ScriptContext::SERVER;
weaponVarArray<context> = mod.Offset(0x997dc0).RCast<WeaponVarInfo*>();
_CalculateWeaponValues<context> = mod.Offset(0x6C8B30).RCast<calculateWeaponValuesType>();
get2ndParamForRecalcModFunc<context> = mod.Offset(0xF0CD0).RCast<get2ndParamForRecalcModFuncType>();
CWeaponX_vftable = mod.Offset(0x98E2B8).RCast<void*>();
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){}
EladNLG marked this conversation as resolved.
Show resolved Hide resolved
30 changes: 30 additions & 0 deletions primedev/mods/modweaponvars.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
enum WeaponVarType : __int8
{
WVT_INTEGER = 1,
WVT_FLOAT32 = 2,
WVT_BOOLEAN = 3,
// NOT SUPPORTED ATM //
WVT_STRING = 4,
WVT_ASSET = 5,
WVT_VECTOR = 6
};

struct WeaponModCallback
{
int priority;
SQObject func;
};

#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);

const int WEAPON_VAR_COUNT = 725;
19 changes: 19 additions & 0 deletions primedev/server/weaponx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once
#include "client/weaponx.h"

inline void* CWeaponX_vftable = nullptr;

#pragma pack(push, 1)
struct CWeaponX
{
public:
void* vftable;
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]; // 0x1410
};
#pragma pack(pop)
2 changes: 2 additions & 0 deletions primedev/squirrel/squirrel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))

g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x3E49B0).RCast<sq_GetEntityConstantType>();
g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance = module.Offset(0x114F0).RCast<sq_getentityfrominstanceType>();
g_pSquirrel<ScriptContext::CLIENT>->__sq_createscriptinstance = module.Offset(0xC20E0).RCast<sq_createscriptinstanceType>();
g_pSquirrel<ScriptContext::UI>->__sq_GetEntityConstant_CBaseEntity =
g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity;
g_pSquirrel<ScriptContext::UI>->__sq_getentityfrominstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance;
Expand Down Expand Up @@ -805,6 +806,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))

g_pSquirrel<ScriptContext::SERVER>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x418AF0).RCast<sq_GetEntityConstantType>();
g_pSquirrel<ScriptContext::SERVER>->__sq_getentityfrominstance = module.Offset(0x1E920).RCast<sq_getentityfrominstanceType>();
g_pSquirrel<ScriptContext::SERVER>->__sq_createscriptinstance = module.Offset(0x43F2F0).RCast<sq_createscriptinstanceType>();

g_pSquirrel<ScriptContext::SERVER>->logger = NS::log::SCRIPT_SV;
// Message buffer stuff
Expand Down
3 changes: 2 additions & 1 deletion primedev/squirrel/squirrel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -447,7 +448,7 @@ inline VoidFunction SQMessageBufferPushArg(Vector3& arg) {
// Vectors
template <ScriptContext context>
inline VoidFunction SQMessageBufferPushArg(SQObject* arg) {
return [arg]{ g_pSquirrel<context>->pushSQObject(g_pSquirrel<context>->m_pSQVM->sqvm, arg); };
return [arg]{ g_pSquirrel<context>->pushobject(g_pSquirrel<context>->m_pSQVM->sqvm, arg); };
}
// Ints
template <ScriptContext context, typename T>
Expand Down
1 change: 1 addition & 0 deletions primedev/squirrel/squirrelclasstypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading