Merge pull request #94 from diamante0018/master

Game logfile & Branding
This commit is contained in:
Maurice Heumann 2022-05-22 16:46:10 +02:00 committed by GitHub
commit d6e5396295
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 321 additions and 42 deletions

View File

@ -6,7 +6,6 @@ namespace game
namespace native
{
Cmd_AddCommand_t Cmd_AddCommand;
Cmd_RemoveCommand_t Cmd_RemoveCommand;
Com_Error_t Com_Error;
@ -16,8 +15,10 @@ namespace game
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;
@ -31,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;
PM_WeaponUseAmmo_t PM_WeaponUseAmmo;
PM_playerTrace_t PM_playerTrace;
PM_trace_t PM_trace;
Jump_ClearState_t Jump_ClearState;
@ -111,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;
}
@ -705,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));
@ -717,9 +710,9 @@ namespace game
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));
@ -754,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));
@ -774,13 +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::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));
@ -829,6 +816,8 @@ namespace game
native::sp::g_clients = reinterpret_cast<native::sp::gclient_s*>(0x1381D48);
native::level = reinterpret_cast<native::level_locals_t*>(SELECT_VALUE(0x0, 0x1C6D4D8, 0x1B21A20));
native::deferredQueue = reinterpret_cast<native::DeferredQueue*>(SELECT_VALUE(0x0, 0x1D55438, 0x0));
}
}

View File

@ -41,6 +41,9 @@ namespace game
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;
@ -80,6 +83,9 @@ namespace game
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;
@ -110,23 +116,20 @@ 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 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);
extern PM_playerTrace_t PM_playerTrace;
@ -179,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
@ -190,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;
}

View File

@ -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,
@ -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
@ -1260,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;
@ -1278,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;

47
src/module/branding.cpp Normal file
View File

@ -0,0 +1,47 @@
#include <std_include.hpp>
#include <loader/module_loader.hpp>
#include <utils/hook.hpp>
#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<const char*>(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)

View File

@ -5,13 +5,29 @@
#include <utils/hook.hpp>
#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;%d;%d;%s;%s\n", guid, ent_num, name, message + 1);
}
else
{
log_file::g_log_printf("sayteam;%d;%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())

140
src/module/log_file.cpp Normal file
View File

@ -0,0 +1,140 @@
#include <std_include.hpp>
#include <loader/module_loader.hpp>
#include <utils/hook.hpp>
#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<const char*>(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<void>(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<void>(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<void(*)()>(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)

25
src/module/log_file.hpp Normal file
View File

@ -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();
};