diff --git a/deps/GSL b/deps/GSL index da01eb2..d9fc52e 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit da01eb28dbb75bed11a51acff0f80ecedd622573 +Subproject commit d9fc52e20e32fcc804502480a781120b210afd41 diff --git a/deps/rapidjson b/deps/rapidjson index 781a4e6..232389d 160000 --- a/deps/rapidjson +++ b/deps/rapidjson @@ -1 +1 @@ -Subproject commit 781a4e667d84aeedbeb8184b7b62425ea66ec59f +Subproject commit 232389d4f1012dddec4ef84861face2d2ba85709 diff --git a/deps/zstd b/deps/zstd index f349d18..9a5e73c 160000 --- a/deps/zstd +++ b/deps/zstd @@ -1 +1 @@ -Subproject commit f349d18776001f39791323e59ef2395983c1cd2e +Subproject commit 9a5e73c74ef2d621992154306ab1ab6ba44ac8fa diff --git a/src/game/game.cpp b/src/game/game.cpp index 7056ef1..f666e36 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -6,7 +6,6 @@ namespace game namespace native { Cmd_AddCommand_t Cmd_AddCommand; - Cmd_RemoveCommand_t Cmd_RemoveCommand; Com_Error_t Com_Error; @@ -14,14 +13,15 @@ namespace game DB_LoadXAssets_t DB_LoadXAssets; Dvar_RegisterBool_t Dvar_RegisterBool; - Dvar_RegisterInt_t Dvar_RegisterInt; + Dvar_RegisterString_t Dvar_RegisterString; Dvar_SetIntByName_t Dvar_SetIntByName; - Dvar_SetFromStringByName_t Dvar_SetFromStringByName; + Dvar_SetString_t Dvar_SetString; G_RunFrame_t G_RunFrame; + G_GetWeaponForName_t G_GetWeaponForName; MSG_ReadData_t MSG_ReadData; @@ -32,48 +32,37 @@ namespace game SL_GetStringOfSize_t SL_GetStringOfSize; Scr_AddEntityNum_t Scr_AddEntityNum; - Scr_Notify_t Scr_Notify; Scr_NotifyLevel_t Scr_NotifyLevel; + Scr_GetNumParam_t Scr_GetNumParam; + Scr_GetString_t Scr_GetString; Sys_ShowConsole_t Sys_ShowConsole; - Sys_Error_t Sys_Error; + Sys_IsServerThread_t Sys_IsServerThread; VM_Notify_t VM_Notify; BG_NetDataChecksum_t BG_NetDataChecksum; LiveStorage_GetPersistentDataDefVersion_t LiveStorage_GetPersistentDataDefVersion; - LiveStorage_GetPersistentDataDefFormatChecksum_t LiveStorage_GetPersistentDataDefFormatChecksum; SV_DirectConnect_t SV_DirectConnect; - SV_ClientEnterWorld_t SV_ClientEnterWorld; - SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString; - SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString; - SV_GameSendServerCommand_t SV_GameSendServerCommand; - SV_SendServerCommand_t SV_SendServerCommand; - Sys_IsServerThread_t Sys_IsServerThread; - XUIDToString_t XUIDToString; SEH_LocalizeTextMessage_t SEH_LocalizeTextMessage; - PM_WeaponUseAmmo_t PM_WeaponUseAmmo; - CM_TransformedCapsuleTrace_t CM_TransformedCapsuleTrace; - Weapon_RocketLauncher_Fire_t Weapon_RocketLauncher_Fire; - + PM_WeaponUseAmmo_t PM_WeaponUseAmmo; PM_playerTrace_t PM_playerTrace; - PM_trace_t PM_trace; Jump_ClearState_t Jump_ClearState; @@ -112,10 +101,14 @@ namespace game gentity_s* g_entities; + level_locals_t* level; + DeferredQueue* deferredQueue; namespace mp { + SV_GetGuid_t SV_GetGuid; + client_t* svs_clients; } @@ -126,9 +119,11 @@ namespace game namespace sp { - sp::IsServerRunning_t IsServerRunning; + IsServerRunning_t IsServerRunning; - sp::gentity_s* g_entities; + gentity_s* g_entities; + + gclient_s* g_clients; } void AddRefToValue(VariableValue* value) @@ -704,7 +699,6 @@ namespace game mode = _mode; native::Cmd_AddCommand = native::Cmd_AddCommand_t(SELECT_VALUE(0x558820, 0x545DF0, 0)); - native::Cmd_RemoveCommand = native::Cmd_RemoveCommand_t(SELECT_VALUE(0x443A30, 0x545E20, 0x4CC060)); native::Com_Error = native::Com_Error_t(SELECT_VALUE(0x425540, 0x555450, 0x4D93F0)); @@ -712,15 +706,16 @@ namespace game native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770)); native::Dvar_RegisterBool = native::Dvar_RegisterBool_t(SELECT_VALUE(0x4914D0, 0x5BE9F0, 0x0)); - native::Dvar_RegisterInt = native::Dvar_RegisterInt_t(SELECT_VALUE(0x48CD40, 0x5BEA40, 0x0)); + native::Dvar_RegisterString = native::Dvar_RegisterString_t(SELECT_VALUE(0x5197F0, 0x5BEC90, 0x0)); native::Dvar_SetIntByName = native::Dvar_SetIntByName_t(SELECT_VALUE(0x5396B0, 0x5BF560, 0x0)); - native::Dvar_SetFromStringByName = native::Dvar_SetFromStringByName_t( SELECT_VALUE(0x4DD090, 0x5BF740, 0x518DF0)); + native::Dvar_SetString = native::Dvar_SetString_t(SELECT_VALUE(0x540570, 0x5BF3E0, 0x0)); native::G_RunFrame = native::G_RunFrame_t(SELECT_VALUE(0x52EAA0, 0x50CB70, 0x48AD60)); + native::G_GetWeaponForName = native::G_GetWeaponForName_t(SELECT_VALUE(0x495E40, 0x531070, 0x0)); native::MSG_ReadData = native::MSG_ReadData_t(SELECT_VALUE(0, 0x5592A0, 0)); @@ -734,6 +729,8 @@ namespace game native::Scr_Notify = native::Scr_Notify_t(SELECT_VALUE(0x4895B0, 0x52B190, 0x0)); native::Scr_NotifyLevel = native::Scr_NotifyLevel_t(SELECT_VALUE(0x445E10, 0x56B6B0, 0x0)); + native::Scr_GetNumParam = native::Scr_GetNumParam_t(SELECT_VALUE(0x4C6FE0, 0x56AA10, 0x0)); + native::Scr_GetString = native::Scr_GetString_t(SELECT_VALUE(0x497530, 0x56A3D0, 0x0)); native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0)); @@ -750,16 +747,12 @@ namespace game SELECT_VALUE(0x0, 0x548D80, 0x4D03D0)); native::SV_DirectConnect = native::SV_DirectConnect_t(SELECT_VALUE(0x0, 0x572750, 0x4F74C0)); - native::SV_ClientEnterWorld = native::SV_ClientEnterWorld_t(SELECT_VALUE(0x0, 0x571100, 0x0)); - native::SV_Cmd_TokenizeString = native::SV_Cmd_TokenizeString_t(SELECT_VALUE(0x0, 0x545D40, 0x0)); - native::SV_Cmd_EndTokenizedString = native::SV_Cmd_EndTokenizedString_t(SELECT_VALUE(0x0, 0x545D70, 0x0)); - native::SV_GameSendServerCommand = native::SV_GameSendServerCommand_t(SELECT_VALUE(0x402130, 0x573220, 0x0)); - native::SV_SendServerCommand = native::SV_SendServerCommand_t(SELECT_VALUE(0x4F6990, 0x575DE0, 0x4FD5A0)); + native::mp::SV_GetGuid = native::mp::SV_GetGuid_t(0x573990); native::Sys_IsServerThread = native::Sys_IsServerThread_t(SELECT_VALUE(0x4CC5A0, 0x55F9A0, 0x0)); @@ -770,16 +763,11 @@ namespace game native::SEH_LocalizeTextMessage = native::SEH_LocalizeTextMessage_t( SELECT_VALUE(0x41EA20, 0x57E240, 0x0)); - native::PM_WeaponUseAmmo = native::PM_WeaponUseAmmo_t(SELECT_VALUE(0x463F80, 0x42E930, 0x0)); - native::CM_TransformedCapsuleTrace = native::CM_TransformedCapsuleTrace_t( SELECT_VALUE(0x4F9B80, 0x541340, 0x0)); - native::Weapon_RocketLauncher_Fire = native::Weapon_RocketLauncher_Fire_t( - SELECT_VALUE(0x48C920, 0x5305D0, 0x0)); - + native::PM_WeaponUseAmmo = native::PM_WeaponUseAmmo_t(SELECT_VALUE(0x463F80, 0x42E930, 0x0)); native::PM_playerTrace = native::PM_playerTrace_t(SELECT_VALUE(0x4CE600, 0x421F00, 0x0)); - native::PM_trace = native::PM_trace_t(SELECT_VALUE(0x544BF0, 0x41CEB0, 0x0)); native::Jump_ClearState = native::Jump_ClearState_t(SELECT_VALUE(0x514CE0, 0x4160F0, 0x0)); @@ -826,6 +814,10 @@ namespace game native::g_entities = reinterpret_cast(SELECT_VALUE(0, 0x1A66E28, 0x191B900)); native::sp::g_entities = reinterpret_cast(0x1197AD8); + native::sp::g_clients = reinterpret_cast(0x1381D48); + + native::level = reinterpret_cast(SELECT_VALUE(0x0, 0x1C6D4D8, 0x1B21A20)); + native::deferredQueue = reinterpret_cast(SELECT_VALUE(0x0, 0x1D55438, 0x0)); } } diff --git a/src/game/game.hpp b/src/game/game.hpp index 4024db5..5e3f0c8 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -31,15 +31,25 @@ namespace game int min, int max, unsigned __int16 flags, const char* description); extern Dvar_RegisterInt_t Dvar_RegisterInt; + typedef const dvar_t* (*Dvar_RegisterString_t)(const char* dvarName, const char* value, + unsigned __int16 flags, const char* description); + extern Dvar_RegisterString_t Dvar_RegisterString; + typedef void (*Dvar_SetIntByName_t)(const char* dvarName, int value); extern Dvar_SetIntByName_t Dvar_SetIntByName; typedef void (*Dvar_SetFromStringByName_t)(const char* dvarName, const char* string); extern Dvar_SetFromStringByName_t Dvar_SetFromStringByName; + typedef void (*Dvar_SetString_t)(const dvar_t* dvar, const char* value); + extern Dvar_SetString_t Dvar_SetString; + typedef int (*G_RunFrame_t)(int, int); extern G_RunFrame_t G_RunFrame; + typedef Weapon (*G_GetWeaponForName_t)(const char* name); + extern G_GetWeaponForName_t G_GetWeaponForName; + typedef void (*MSG_ReadData_t)(msg_t* msg, void* data, int len); extern MSG_ReadData_t MSG_ReadData; @@ -61,12 +71,21 @@ namespace game typedef void (*Scr_NotifyLevel_t)(unsigned int stringValue, unsigned int paramcount); extern Scr_NotifyLevel_t Scr_NotifyLevel; + typedef unsigned int (*Scr_GetNumParam_t)(); + extern Scr_GetNumParam_t Scr_GetNumParam; + + typedef const char* (*Scr_GetString_t)(unsigned int index); + extern Scr_GetString_t Scr_GetString; + typedef void (*Sys_ShowConsole_t)(); extern Sys_ShowConsole_t Sys_ShowConsole; typedef void (*Sys_Error_t)(const char* error, ...); extern Sys_Error_t Sys_Error; + typedef bool (*Sys_IsServerThread_t)(); + extern Sys_IsServerThread_t Sys_IsServerThread; + typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top); extern VM_Notify_t VM_Notify; @@ -97,26 +116,19 @@ namespace game typedef void (*SV_SendServerCommand_t)(dedi::client_t* cl, svscmd_type type, const char* fmt, ...); extern SV_SendServerCommand_t SV_SendServerCommand; - typedef bool (*Sys_IsServerThread_t)(); - extern Sys_IsServerThread_t Sys_IsServerThread; - typedef void (*XUIDToString_t)(const unsigned __int64* xuid, char* str); extern XUIDToString_t XUIDToString; typedef char* (*SEH_LocalizeTextMessage_t)(const char* pszInputBuffer, const char* pszMessageType, msgLocErrType_t errType); extern SEH_LocalizeTextMessage_t SEH_LocalizeTextMessage; - typedef void (*PM_WeaponUseAmmo_t)(playerState_s* ps, const Weapon weapon, bool isAlternate, int amount, PlayerHandIndex hand); - extern PM_WeaponUseAmmo_t PM_WeaponUseAmmo; - typedef void (*CM_TransformedCapsuleTrace_t)(trace_t* results, const float* start, const float* end, const Bounds* bounds, const Bounds* capsule, int contents, const float* origin, const float* angles); extern CM_TransformedCapsuleTrace_t CM_TransformedCapsuleTrace; - typedef gentity_s* (*Weapon_RocketLauncher_Fire_t)(gentity_s* ent, const Weapon weapon, float spread, - weaponParms* wp, const float* gunVel, missileFireParms* fireParms, missileFireParms* magicBullet); - extern Weapon_RocketLauncher_Fire_t Weapon_RocketLauncher_Fire; + typedef void (*PM_WeaponUseAmmo_t)(playerState_s* ps, const Weapon weapon, bool isAlternate, int amount, PlayerHandIndex hand); + extern PM_WeaponUseAmmo_t PM_WeaponUseAmmo; typedef void (*PM_playerTrace_t)(pmove_t* pm, trace_t* results, const float* start, const float* end, const Bounds* bounds, int passEntityNum, int contentMask); @@ -170,6 +182,8 @@ namespace game constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1u; extern gentity_s* g_entities; + extern level_locals_t* level; + extern DeferredQueue* deferredQueue; // PM Global Definitions & Functions @@ -181,6 +195,9 @@ namespace game namespace mp { + typedef char* (*SV_GetGuid_t)(int clientNum); + extern SV_GetGuid_t SV_GetGuid; + extern client_t* svs_clients; } @@ -194,7 +211,9 @@ namespace game typedef bool (*IsServerRunning_t)(); extern IsServerRunning_t IsServerRunning; - extern sp::gentity_s* g_entities; + extern gentity_s* g_entities; + + extern gclient_s* g_clients; } void AddRefToValue(VariableValue* value); diff --git a/src/game/structs.hpp b/src/game/structs.hpp index e3be37d..8536b94 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -400,7 +400,14 @@ namespace game LOCAL_CLIENT_3 = 3, LOCAL_CLIENT_LAST = 3, LOCAL_CLIENT_COUNT = 4, - LOCAL_CLIENT_INVALID = -1, + }; + + enum fsMode_t + { + FS_READ, + FS_WRITE, + FS_APPEND, + FS_APPEND_SYNC, }; enum msgLocErrType_t @@ -597,6 +604,7 @@ namespace game enum dvar_flags : std::uint16_t { + DVAR_NONE = 0, DVAR_ARCHIVE = 1 << 0, DVAR_LATCH = 1 << 1, DVAR_CHEAT = 1 << 2, @@ -758,18 +766,18 @@ namespace game struct Weapon_s { - unsigned int padding : 8; - unsigned int scopeVariation : 3; - unsigned int weaponOthers : 4; - unsigned int weaponUnderBarrels : 2; - unsigned int weaponScopes : 3; unsigned int weaponIdx : 8; unsigned int weaponVariation : 4; + unsigned int weaponScopes : 3; + unsigned int weaponUnderBarrels : 2; + unsigned int weaponOthers : 4; + unsigned int scopeVariation : 3; + unsigned int padding : 8; }; union Weapon { - Weapon_s _s_0; + Weapon_s __s0; unsigned int data; }; @@ -1145,6 +1153,49 @@ namespace game static_assert(sizeof(clientHeader_t) == 0x66C); + enum objectiveState_t + { + OBJST_EMPTY = 0x0, + OBJST_ACTIVE = 0x1, + OBJST_INVISIBLE = 0x2, + OBJST_DONE = 0x3, + OBJST_CURRENT = 0x4, + OBJST_FAILED = 0x5, + OBJST_NUMSTATES = 0x6, + }; + + struct objective_t + { + objectiveState_t state; + float origin[3]; + int entNum; + int teamNum; + int clientNum; + int invertVisibilityByClientNum; + int icon; + }; + + static_assert(sizeof(objective_t) == 0x24); + + struct level_locals_t + { + gclient_s* clients; + gentity_s* gentities; + int num_entities; + gentity_s* firstFreeEnt; + gentity_s* lastFreeEnt; + void* turrets; + int initializing; + int clientIsSpawning; + objective_t objectives[32]; + int maxclients; + int framenum; + int time; + unsigned char __pad0[0x2BD4]; + }; + + static_assert(sizeof(level_locals_t) == 0x3080); + namespace mp { struct client_t @@ -1154,14 +1205,15 @@ namespace game char userinfo[1024]; // 0x670 unsigned char __pad0[0x209B8]; gentity_s* gentity; // 0x21428 - unsigned char __pad1[0x20886]; + char name[16]; // 0x2142C + unsigned char __pad1[0x20876]; unsigned __int16 scriptId; // 0x41CB2 int bIsTestClient; // 0x41CB4 int serverId; // 0x41CB8 unsigned char __pad2[0x369DC]; }; - static_assert(sizeof(mp::client_t) == 0x78698); + static_assert(sizeof(client_t) == 0x78698); } namespace dedi @@ -1183,11 +1235,70 @@ namespace game namespace sp { + struct usercmd_s + { + int serverTime; + int buttons; + int angles[3]; + Weapon weapon; + Weapon offHand; + char forwardmove; + char rightmove; + unsigned char upmove; + unsigned char downmove; + char pitchmove; + char yawmove; + float gunPitch; + float gunYaw; + float gunXOfs; + float gunYOfs; + float gunZOfs; + short meleeChargeYaw; + unsigned char meleeChargeDist; + char selectedLoc[2]; + unsigned char selectedLocAngle; + char remoteControlAngles[2]; + }; + + static_assert(sizeof(usercmd_s) == 0x40); + + struct clientPersistent_t + { + usercmd_s cmd; + usercmd_s oldcmd; + int maxHealth; + float moveSpeedScaleMultiplier; + int motionTrackerEnabled; + char playerName[0x20]; + }; + + static_assert(sizeof(clientPersistent_t) == 0xAC); + + struct playerState_s + { + int commandTime; + int pm_type; + int pm_time; + int pm_flags; + int otherFlags; + int linkFlags; + int bobCycle; + float origin[3]; + float velocity[3]; + unsigned char __pad0[0xAD24]; + }; + + static_assert(sizeof(playerState_s) == 0xAD58); + struct gclient_s { - unsigned char __pad0[0xAE04]; + playerState_s ps; + clientPersistent_t pers; int flags; - }; // Warning, incorrect size + unsigned char __pad1[0x2BC]; + }; + + static_assert(sizeof(gclient_s) == 0xB0C4); struct entityState_s { @@ -1201,11 +1312,11 @@ namespace game struct entityShared_t { - unsigned __int8 isLinked; - unsigned __int8 modelType; - unsigned __int8 svFlags; - unsigned __int8 eventType; - unsigned __int8 isInUse; + unsigned char isLinked; + unsigned char modelType; + unsigned char svFlags; + unsigned char eventType; + unsigned char isInUse; Bounds box; int contents; Bounds absBox; @@ -1219,9 +1330,9 @@ namespace game struct gentity_s { - sp::entityState_s s; - sp::entityShared_t r; - sp::gclient_s* client; // 0x10C + entityState_s s; + entityShared_t r; + gclient_s* client; // 0x10C unsigned char __pad0[0x2C]; int flags; int clipmask; diff --git a/src/module/branding.cpp b/src/module/branding.cpp new file mode 100644 index 0000000..2a7a823 --- /dev/null +++ b/src/module/branding.cpp @@ -0,0 +1,47 @@ +#include +#include + +#include + +#include "game/game.hpp" + +static char* com_get_build_version_stub() +{ + static char buf[128]; + + const auto version_number = SELECT_VALUE(0x1CD, 0x5EC0E, 0x5EC0E); + + _snprintf_s(buf, _TRUNCATE, "%d %s", version_number, __DATE__); + + return buf; +} + +static int com_get_build_version_dedi_stub(char* buf, const char* fmt, int version_number, const char* /*date*/) +{ + return _snprintf_s(buf, 0x80, _TRUNCATE, fmt, version_number, __DATE__); +} + +class branding final : public module +{ +public: + void post_load() override + { + if (game::is_dedi()) this->patch_dedi(); + else this->add_branding(); + + // gamedate dvar + utils::hook::set(SELECT_VALUE(0x5C223B, 0x50B0F4, 0x48844F), __DATE__); + } + + static void patch_dedi() + { + utils::hook(0x4DAB99, com_get_build_version_dedi_stub, HOOK_CALL).install()->quick(); + } + + static void add_branding() + { + utils::hook(SELECT_VALUE(0x50BBD0, 0x53B4B0, 0x0), com_get_build_version_stub, HOOK_JUMP).install()->quick(); + } +}; + +REGISTER_MODULE(branding) diff --git a/src/module/chat.cpp b/src/module/chat.cpp index 2572cdb..526a2f5 100644 --- a/src/module/chat.cpp +++ b/src/module/chat.cpp @@ -5,13 +5,29 @@ #include +#include "log_file.hpp" + static void notify_on_say(game::native::gentity_s* ent, int mode, const char* message) { + const auto ent_num = ent->s.number; + game::native::Scr_AddString(message + 1); // First character has nothing to do with actual message game::native::Scr_AddInt(mode); - game::native::Scr_AddEntityNum(ent->s.number, 0); + game::native::Scr_AddEntityNum(ent_num, 0); game::native::Scr_NotifyLevel(game::native::SL_GetString("say", 0), 3); + + const auto* guid = game::native::mp::SV_GetGuid(ent_num); + const auto* name = game::native::mp::svs_clients[ent_num].name; + + if (mode == 0) + { + log_file::g_log_printf("say;%s;%d;%s;%s\n", guid, ent_num, name, message + 1); + } + else + { + log_file::g_log_printf("sayteam;%s;%d;%s;%s\n", guid, ent_num, name, message + 1); + } } static __declspec(naked) void g_say_stub() @@ -36,6 +52,8 @@ static __declspec(naked) void g_say_stub() class chat final : public module { public: + static_assert(offsetof(game::native::mp::client_t, name) == 0x2142C); + void post_load() override { if (game::is_mp()) diff --git a/src/module/command.cpp b/src/module/command.cpp index c637b04..43e034d 100644 --- a/src/module/command.cpp +++ b/src/module/command.cpp @@ -233,7 +233,7 @@ __declspec(naked) void command::client_command_dedi_stub() // Between ufo/noclip functions and their mp counterpart is that I reversed the 'CG' type void command::add_sp_commands() { - add("noclip", []() + add("noclip", [] { if (!game::native::sp::IsServerRunning()) return; @@ -251,7 +251,7 @@ void command::add_sp_commands() printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "noclip print", game::native::LOCMSG_SAFE)); }); - add("ufo", []() + add("ufo", [] { if (!game::native::sp::IsServerRunning()) return; @@ -269,7 +269,7 @@ void command::add_sp_commands() printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "ufo print", game::native::LOCMSG_SAFE)); }); - add("god", []() + add("god", [] { if (!game::native::sp::IsServerRunning()) return; @@ -287,7 +287,7 @@ void command::add_sp_commands() printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "god print", game::native::LOCMSG_SAFE)); }); - add("demigod", []() + add("demigod", [] { if (!game::native::sp::IsServerRunning()) return; @@ -305,7 +305,7 @@ void command::add_sp_commands() printf("%s\n", game::native::SEH_LocalizeTextMessage(msg, "demigod print", game::native::LOCMSG_SAFE)); }); - add("notarget", []() + add("notarget", [] { if (!game::native::sp::IsServerRunning()) return; diff --git a/src/module/fov.cpp b/src/module/fov.cpp index 19ae102..5fca813 100644 --- a/src/module/fov.cpp +++ b/src/module/fov.cpp @@ -28,7 +28,7 @@ public: private: static void set_server_command_dvar_stub(const char* dvar, const char* value) { - if (strcmp(dvar, "cg_fov") != 0 || strcmp(value, "65") != 0) + if (strcmp(dvar, "cg_fov") != 0) { game::native::Dvar_SetFromStringByName(dvar, value); } diff --git a/src/module/log_file.cpp b/src/module/log_file.cpp new file mode 100644 index 0000000..d69d617 --- /dev/null +++ b/src/module/log_file.cpp @@ -0,0 +1,140 @@ +#include +#include + +#include + +#include "game/game.hpp" +#include "log_file.hpp" +#include "scheduler.hpp" + +const game::native::dvar_t* log_file::g_log; +const game::native::dvar_t* log_file::g_logSync; + +FILE* log_file::log_fsh = nullptr; + +void log_file::g_log_printf(const char* fmt, ...) +{ + char buf[1024] = {0}; + char out[1024] = {0}; + + va_list va; + va_start(va, fmt); + vsnprintf_s(buf, _TRUNCATE, fmt, va); + va_end(va); + + if (log_fsh == nullptr) + { + return; + } + + _snprintf_s(out, _TRUNCATE, "%3i:%i%i %s", + game::native::level->time / 1000 / 60, + game::native::level->time / 1000 % 60 / 10, + game::native::level->time / 1000 % 60 % 10, + buf); + + fprintf(log_fsh, "%s", out); + fflush(log_fsh); +} + +void log_file::gscr_log_print() +{ + char buf[1024] = {0}; + std::size_t out_chars = 0; + + for (std::size_t i = 0; i < game::native::Scr_GetNumParam(); ++i) + { + const auto* value = game::native::Scr_GetString(i); + const auto len = std::strlen(value); + + out_chars += len; + if (out_chars >= sizeof(buf)) + { + // Do not overflow the buffer + break; + } + + strncat_s(buf, value, _TRUNCATE); + } + + g_log_printf("%s", buf); +} + +void log_file::g_init_game_stub() +{ + printf("------- Game Initialization -------\n"); + printf("gamename: %s", reinterpret_cast(0x7FFC68)); + printf("gamedate: %s\n", __DATE__); + + const auto* log = g_log->current.string; + + if (*log == '\0') + { + printf("Not logging to disk.\n"); + } + else + { + log_fsh = _fsopen(log, "a", _SH_DENYWR); + + if (log_fsh == nullptr) + { + printf("WARNING: Couldn't open logfile: %s\n", log); + } + else + { + printf("Logging to disk: '%s'.\n", log); + g_log_printf("------------------------------------------------------------\n"); + g_log_printf("InitGame\n"); + } + } + + utils::hook::invoke(0x5C2800); +} + +void log_file::g_shutdown_game_stub(int free_scripts) +{ + printf("==== ShutdownGame (%d) ====\n", free_scripts); + + if (log_fsh != nullptr) + { + g_log_printf("ShutdownGame:\n"); + g_log_printf("------------------------------------------------------------\n"); + + fclose(log_fsh); + log_fsh = nullptr; + } + + utils::hook::invoke(0x50C100, free_scripts); +} + +void log_file::exit_level_stub() +{ + printf("ExitLevel: executed\n"); +} + +void log_file::post_load() +{ + if (!game::is_mp()) + { + return; + } + + utils::hook::set(0x8AC858, gscr_log_print); + + utils::hook(0x50D135, g_init_game_stub, HOOK_CALL).install()->quick(); + + utils::hook(0x573C82, g_shutdown_game_stub, HOOK_CALL).install()->quick(); + utils::hook(0x573D3A, g_shutdown_game_stub, HOOK_CALL).install()->quick(); + + utils::hook(0x50D5F4, exit_level_stub, HOOK_JUMP).install()->quick(); + + scheduler::once([] + { + g_log = game::native::Dvar_RegisterString("g_log", "games_mp.log", + game::native::DVAR_ARCHIVE, "Log file name"); + g_logSync = game::native::Dvar_RegisterBool("g_logSync", false, + game::native::DVAR_NONE, "Enable synchronous logging"); + }, scheduler::pipeline::main); +} + +REGISTER_MODULE(log_file) diff --git a/src/module/log_file.hpp b/src/module/log_file.hpp new file mode 100644 index 0000000..70d218b --- /dev/null +++ b/src/module/log_file.hpp @@ -0,0 +1,25 @@ +#pragma once + +class log_file final : public module +{ +public: + static_assert(offsetof(game::native::level_locals_t, time) == 0x4A8); + + void post_load() override; + + static void g_log_printf(const char* fmt, ...); + +private: + static const game::native::dvar_t* g_log; + static const game::native::dvar_t* g_logSync; + + static FILE* log_fsh; + + static void gscr_log_print(); + + static void g_init_game_stub(); + + static void g_shutdown_game_stub(int free_scripts); + + static void exit_level_stub(); +}; diff --git a/src/module/player_movement.cpp b/src/module/player_movement.cpp index c03c8a9..1235f8f 100644 --- a/src/module/player_movement.cpp +++ b/src/module/player_movement.cpp @@ -67,7 +67,7 @@ int player_movement::stuck_in_client_stub(game::native::gentity_s* self) { if (pm_playerEjection->current.enabled) { - return reinterpret_cast(0x4F8930)(self); + return utils::hook::invoke(0x4F8930, self); } return 0; @@ -84,18 +84,35 @@ void player_movement::cm_transformed_capsule_trace_stub(game::native::trace_t* r } } -game::native::gentity_s* player_movement::weapon_rocket_launcher_fire_stub(game::native::gentity_s* ent, +game::native::gentity_s* player_movement::weapon_rocket_launcher_fire_mp_stub(game::native::gentity_s* ent, const game::native::Weapon weapon, float spread, game::native::weaponParms* wp, const float* gun_vel, game::native::missileFireParms* fire_parms, game::native::missileFireParms* magic_bullet) { - auto* result = game::native::Weapon_RocketLauncher_Fire(ent, weapon, spread, wp, + auto* result = utils::hook::invoke(0x5305D0, ent, weapon, spread, wp, gun_vel, fire_parms, magic_bullet); if (ent->client != nullptr && pm_rocketJump->current.enabled) { - ent->client->ps.velocity[0] += (0 - wp->forward[0]) * 64.0f; - ent->client->ps.velocity[1] += (0 - wp->forward[1]) * 64.0f; - ent->client->ps.velocity[2] += (0 - wp->forward[2]) * 64.0f; + ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * 64.0f; + ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * 64.0f; + ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * 64.0f; + } + + return result; +} + +game::native::sp::gentity_s* player_movement::weapon_rocket_launcher_fire_sp_stub(game::native::sp::gentity_s* ent, + const game::native::Weapon weapon, float spread, game::native::weaponParms* wp, const float* gun_vel, + game::native::missileFireParms* fire_parms, game::native::missileFireParms* magic_bullet) +{ + auto* result = utils::hook::invoke(0x48C920, ent, weapon, spread, wp, + gun_vel, fire_parms, magic_bullet); + + if (ent->client != nullptr && pm_rocketJump->current.enabled) + { + ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * 64.0f; + ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * 64.0f; + ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * 64.0f; } return result; @@ -458,8 +475,6 @@ void player_movement::patch_mp() { pm_playerEjection = game::native::Dvar_RegisterBool("pm_playerEjection", true, game::native::DVAR_CODINFO, "Push intersecting players away from each other"); - pm_rocketJump = game::native::Dvar_RegisterBool("pm_rocketJump", - false, game::native::DVAR_CODINFO, "CoD4 rocket jumps"); // Name is correct, SP registers this dvar in BG_RegisterDvars but still names it just "g_gravity" bg_gravity = game::native::Dvar_RegisterFloat("g_gravity", 800.0f, 1.0f, std::numeric_limits::max(), game::native::DVAR_CODINFO, "Gravity in inches per second per second"); @@ -484,7 +499,7 @@ void player_movement::patch_mp() utils::hook(0x57CF45, cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity utils::hook(0x482C1B, cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity - utils::hook(0x530CCB, weapon_rocket_launcher_fire_stub, HOOK_CALL).install()->quick(); // FireWeapon + utils::hook(0x530CCB, weapon_rocket_launcher_fire_mp_stub, HOOK_CALL).install()->quick(); // FireWeapon utils::hook(0x422861, pm_player_trace_stub, HOOK_CALL).install()->quick(); // PM_JitterPoint utils::hook(0x4228B5, pm_player_trace_stub, HOOK_CALL).install()->quick(); // PM_JitterPoint @@ -545,6 +560,8 @@ void player_movement::patch_sp() utils::hook(0x41F9A6, cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity utils::hook(0x57B14F, cm_transformed_capsule_trace_stub, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity + utils::hook(0x45E606, weapon_rocket_launcher_fire_sp_stub, HOOK_CALL).install()->quick(); // FireWeapon + utils::hook(0x643F84, pm_player_trace_stub, HOOK_CALL).install()->quick(); // PM_JitterPoint utils::hook(0x643FDB, pm_player_trace_stub, HOOK_CALL).install()->quick(); // PM_JitterPoint @@ -582,6 +599,8 @@ void player_movement::register_common_dvars() true, game::native::DVAR_CODINFO, "Push intersecting players away from each other"); pm_elevators = game::native::Dvar_RegisterBool("pm_elevators", false, game::native::DVAR_CODINFO, "CoD4 Elevators"); + pm_rocketJump = game::native::Dvar_RegisterBool("pm_rocketJump", + false, game::native::DVAR_CODINFO, "CoD4 rocket jumps"); // Jump dvars jump_enableFallDamage = game::native::Dvar_RegisterBool("jump_enableFallDamage", diff --git a/src/module/player_movement.hpp b/src/module/player_movement.hpp index 0a00e16..1ec33ce 100644 --- a/src/module/player_movement.hpp +++ b/src/module/player_movement.hpp @@ -58,7 +58,11 @@ private: const float* end, const game::native::Bounds* bounds, const game::native::Bounds* capsule, int contents, const float* origin, const float* angles); - static game::native::gentity_s* weapon_rocket_launcher_fire_stub(game::native::gentity_s* ent, + static game::native::gentity_s* weapon_rocket_launcher_fire_mp_stub(game::native::gentity_s* ent, + const game::native::Weapon weapon, float spread, game::native::weaponParms* wp, + const float* gun_vel, game::native::missileFireParms* fire_parms, game::native::missileFireParms* magic_bullet); + + static game::native::sp::gentity_s* weapon_rocket_launcher_fire_sp_stub(game::native::sp::gentity_s* ent, const game::native::Weapon weapon, float spread, game::native::weaponParms* wp, const float* gun_vel, game::native::missileFireParms* fire_parms, game::native::missileFireParms* magic_bullet); diff --git a/src/module/scheduler.cpp b/src/module/scheduler.cpp index 7c87997..c9256bb 100644 --- a/src/module/scheduler.cpp +++ b/src/module/scheduler.cpp @@ -95,19 +95,19 @@ void scheduler::execute(const pipeline type) void scheduler::r_end_frame_stub() { - reinterpret_cast(SELECT_VALUE(0x4193D0, 0x67F840, 0x0))(); + utils::hook::invoke(SELECT_VALUE(0x4193D0, 0x67F840, 0x0)); execute(pipeline::renderer); } void scheduler::g_glass_update_stub() { - reinterpret_cast(SELECT_VALUE(0x4E3730, 0x505BB0, 0x481EA0))(); + utils::hook::invoke(SELECT_VALUE(0x4E3730, 0x505BB0, 0x481EA0)); execute(pipeline::server); } void scheduler::main_frame_stub() { - reinterpret_cast(SELECT_VALUE(0x458600, 0x556470, 0x4DB070))(); + utils::hook::invoke(SELECT_VALUE(0x458600, 0x556470, 0x4DB070)); execute(pipeline::main); } diff --git a/src/module/test_clients.cpp b/src/module/test_clients.cpp index 9ccae59..5a499b5 100644 --- a/src/module/test_clients.cpp +++ b/src/module/test_clients.cpp @@ -135,7 +135,7 @@ void test_clients::spawn(const int count) void test_clients::scr_shutdown_system_mp_stub(unsigned char sys) { game::native::SV_DropAllBots(); - reinterpret_cast(0x569E30)(sys); + utils::hook::invoke(0x569E30, sys); } __declspec(naked) void test_clients::reset_reliable_mp() diff --git a/src/utils/hook.hpp b/src/utils/hook.hpp index 91f89c0..7b81954 100644 --- a/src/utils/hook.hpp +++ b/src/utils/hook.hpp @@ -164,6 +164,18 @@ namespace utils return set(reinterpret_cast(place), value); } + template + static T invoke(size_t func, Args ... args) + { + return reinterpret_cast(func)(args...); + } + + template + static T invoke(void* func, Args ... args) + { + return static_cast(func)(args...); + } + private: bool initialized_; bool installed_;