diff --git a/src/a.cpp b/src/a.cpp index 4948926..b7cddba 100644 --- a/src/a.cpp +++ b/src/a.cpp @@ -2,4 +2,4 @@ // The naming of the file enforces early linking and thus // a better placement in the tls segment -__declspec(thread) char tls_data[TLS_PAYLOAD_SIZE]; \ No newline at end of file +__declspec(thread) char tls_data[TLS_PAYLOAD_SIZE]; diff --git a/src/game/game.cpp b/src/game/game.cpp index fbe2096..7056ef1 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -34,9 +34,12 @@ namespace game Scr_AddEntityNum_t Scr_AddEntityNum; Scr_Notify_t Scr_Notify; + Scr_NotifyLevel_t Scr_NotifyLevel; Sys_ShowConsole_t Sys_ShowConsole; + Sys_Error_t Sys_Error; + VM_Notify_t VM_Notify; BG_NetDataChecksum_t BG_NetDataChecksum; @@ -92,6 +95,7 @@ namespace game char** scrMemTreePub; char* scrMemTreeGlob; + scrVarPub_t* scr_VarPub; scrVmPub_t* scr_VmPub; scr_call_t* scr_instanceFunctions; @@ -255,7 +259,7 @@ namespace game (SELECT_VALUE(0x539550, 0x5BDCC0, 0x0))(dvarName); } - __declspec(naked) const dvar_t* Dvar_RegisterVariant(const char* dvarName, unsigned char type, + __declspec(naked) const dvar_t* dvar_register_variant_dedicated(const char* dvar_name, unsigned char type, unsigned __int16 flags, DvarValue value, DvarLimits domain, const char* description) { static DWORD func = 0x531F70; @@ -307,10 +311,23 @@ namespace game dvar_value.value = value; - return Dvar_RegisterVariant(dvarName, dvar_type::DVAR_TYPE_FLOAT, + return dvar_register_variant_dedicated(dvarName, dvar_type::DVAR_TYPE_FLOAT, flags, dvar_value, domain, description); } + void IncInParam() + { + Scr_ClearOutParams(); + + if (scr_VmPub->top == scr_VmPub->maxStack) + { + Sys_Error("Internal script stack overflow"); + } + + scr_VmPub->top++; + scr_VmPub->inparamcount++; + } + const float* Scr_AllocVector(const float* v) { const auto mem = static_cast(MT_Alloc(16, 2)); @@ -396,16 +413,16 @@ namespace game { if (is_mp()) { - return scr_notify_id_multiplayer(id, stringValue, paramcount); + scr_notify_id_multiplayer(id, stringValue, paramcount); } else if (is_sp()) { - return scr_notify_id_singleplayer(id, stringValue, paramcount); + scr_notify_id_singleplayer(id, stringValue, paramcount); } else { - return reinterpret_cast(0x4EFAA0)( - id, stringValue, paramcount); + reinterpret_cast(0x4EFAA0) + (id, stringValue, paramcount); } } @@ -437,7 +454,7 @@ namespace game } } - __declspec(naked) void scr_add_string_dedi(const char* value) + __declspec(naked) void scr_add_string_dedicated(const char* value) { static DWORD func = 0x4F1010; @@ -453,15 +470,23 @@ namespace game { if (is_dedi()) { - scr_add_string_dedi(value); + scr_add_string_dedicated(value); } - else if (is_mp()) + else { reinterpret_cast - (0x56AC00)(value); + (SELECT_VALUE(0x4A5600, 0x56AC00, 0x0))(value); } } + void Scr_AddInt(int value) + { + IncInParam(); + + scr_VmPub->top->type = SCRIPT_INTEGER; + scr_VmPub->top->u.intValue = value; + } + const char* SL_ConvertToString(const unsigned int stringValue) { if (!stringValue) return nullptr; @@ -472,7 +497,7 @@ namespace game unsigned int SL_GetString(const char* str, const unsigned int user) { - return SL_GetStringOfSize(str, user, strlen(str) + 1, 7); + return SL_GetStringOfSize(str, user, std::strlen(str) + 1, 7); } __declspec(naked) void sv_send_client_game_state_mp(mp::client_t* /*client*/) @@ -544,7 +569,7 @@ namespace game } } - __declspec(naked) void client_command_dedi(int client_num) + __declspec(naked) void client_command_dedicated(int client_num) { static DWORD func = 0x47EA40; @@ -560,7 +585,7 @@ namespace game { if (is_dedi()) { - client_command_dedi(clientNum); + client_command_dedicated(clientNum); } else if (is_mp()) { @@ -578,7 +603,7 @@ namespace game addr->type = type; } - __declspec(naked) void cbuf_add_text_dedi(LocalClientNum_t local_client_num, const char* text) + __declspec(naked) void cbuf_add_text_dedicated(LocalClientNum_t local_client_num, const char* text) { static DWORD func = 0x4CB5D0; @@ -596,7 +621,7 @@ namespace game { if (is_dedi()) { - cbuf_add_text_dedi(localClientNum, text); + cbuf_add_text_dedicated(localClientNum, text); } else { @@ -605,7 +630,7 @@ namespace game } } - __declspec(naked) void teleport_player_dedi(gentity_s* player, float* origin, float* angles) + __declspec(naked) void teleport_player_dedicated(gentity_s* player, float* origin, float* angles) { static DWORD func = 0x48B840; @@ -624,7 +649,7 @@ namespace game { if (is_dedi()) { - teleport_player_dedi(player, origin, angles); + teleport_player_dedicated(player, origin, angles); } else if (is_mp()) { @@ -708,9 +733,12 @@ namespace game native::Scr_AddEntityNum = native::Scr_AddEntityNum_t(SELECT_VALUE(0x0, 0x56ABC0, 0x4EA2F0)); 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::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0)); + native::Sys_Error = native::Sys_Error_t(SELECT_VALUE(0x490D90, 0x5CC3B0, 0x539590)); + native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450)); native::BG_NetDataChecksum = native::BG_NetDataChecksum_t(SELECT_VALUE(0x0, 0x41BB20, 0x0)); @@ -774,6 +802,7 @@ namespace game native::scrMemTreePub = reinterpret_cast(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4)); native::scrMemTreeGlob = reinterpret_cast(SELECT_VALUE(0x186DA00, 0x1D6FF00, 0x1C16600)); + native::scr_VarPub = reinterpret_cast(SELECT_VALUE(0x0, 0x208E188, 0x1CD8720)); native::scr_VmPub = reinterpret_cast(SELECT_VALUE(0x1BF2580, 0x20B4A80, 0x1F5B080)); native::scr_instanceFunctions = reinterpret_cast(SELECT_VALUE(0x184CDB0, 0x1D4F258, diff --git a/src/game/game.hpp b/src/game/game.hpp index 4d9ac39..4024db5 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -55,12 +55,18 @@ namespace game typedef void (*Scr_AddEntityNum_t)(int entnum, unsigned int classnum); extern Scr_AddEntityNum_t Scr_AddEntityNum; - typedef void (*Scr_Notify_t)(gentity_s* ent, unsigned __int16 stringValue, unsigned int paramcount); + typedef void (*Scr_Notify_t)(gentity_s* ent, scr_string_t, unsigned int paramcount); extern Scr_Notify_t Scr_Notify; + typedef void (*Scr_NotifyLevel_t)(unsigned int stringValue, unsigned int paramcount); + extern Scr_NotifyLevel_t Scr_NotifyLevel; + 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 void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top); extern VM_Notify_t VM_Notify; @@ -145,6 +151,7 @@ namespace game extern char** scrMemTreePub; extern char* scrMemTreeGlob; + extern scrVarPub_t* scr_VarPub; extern scrVmPub_t* scr_VmPub; extern scr_call_t* scr_instanceFunctions; @@ -201,7 +208,6 @@ namespace game void* MT_Alloc(int numBytes, int type); dvar_t* Dvar_FindVar(const char* dvarName); - const dvar_t* Dvar_RegisterVariant(const char* dvarName, unsigned char type, unsigned __int16 flags, DvarValue value, DvarLimits domain, const char* description); const dvar_t* Dvar_RegisterFloat(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description); const float* Scr_AllocVector(const float* v); @@ -211,6 +217,7 @@ namespace game void Scr_NotifyId(unsigned int id, unsigned int stringValue, unsigned int paramcount); int Scr_SetObjectField(unsigned int classnum, int entnum, int offset); void Scr_AddString(const char* value); + void Scr_AddInt(int value); const char* SL_ConvertToString(unsigned int stringValue); unsigned int SL_GetString(const char* str, unsigned int user); diff --git a/src/game/scripting/parameters.cpp b/src/game/scripting/parameters.cpp index 2531d3c..a7ac408 100644 --- a/src/game/scripting/parameters.cpp +++ b/src/game/scripting/parameters.cpp @@ -14,19 +14,19 @@ namespace game::scripting const std::string string = native::SL_ConvertToString(value.u.stringValue); return chaiscript::var(string); } - else if (value.type == native::SCRIPT_FLOAT) + if (value.type == native::SCRIPT_FLOAT) { return chaiscript::var(value.u.floatValue); } - else if (value.type == native::SCRIPT_INTEGER) + if (value.type == native::SCRIPT_INTEGER) { return chaiscript::var(value.u.intValue); } - else if (value.type == native::SCRIPT_OBJECT) + if (value.type == native::SCRIPT_OBJECT) { return chaiscript::var(entity(this->context_, value.u.entityId)); } - else if (value.type == native::SCRIPT_VECTOR) + if (value.type == native::SCRIPT_VECTOR) { std::vector values; values.push_back(chaiscript::var(value.u.vectorValue[0])); @@ -46,7 +46,7 @@ namespace game::scripting native::Scr_ClearOutParams(); } - if (native::scr_VmPub->top == native::scr_VmPub->maxstack) + if (native::scr_VmPub->top == native::scr_VmPub->maxStack) { throw std::runtime_error("Internal script stack overflow"); } @@ -75,6 +75,12 @@ namespace game::scripting value_ptr->type = native::SCRIPT_INTEGER; value_ptr->u.intValue = real_value; } + else if (value.get_type_info() == typeid(unsigned int)) + { + const auto real_value = this->context_->get_chai()->boxed_cast(value); + value_ptr->type = native::SCRIPT_INTEGER; + value_ptr->u.intValue = static_cast(real_value); + } else if (value.get_type_info() == typeid(bool)) { const auto real_value = this->context_->get_chai()->boxed_cast(value); diff --git a/src/game/scripting/stack_isolation.cpp b/src/game/scripting/stack_isolation.cpp index fee99cb..604c210 100644 --- a/src/game/scripting/stack_isolation.cpp +++ b/src/game/scripting/stack_isolation.cpp @@ -8,10 +8,10 @@ namespace game::scripting this->in_param_count_ = native::scr_VmPub->inparamcount; this->out_param_count_ = native::scr_VmPub->outparamcount; this->top_ = native::scr_VmPub->top; - this->max_stack_ = native::scr_VmPub->maxstack; + this->max_stack_ = native::scr_VmPub->maxStack; native::scr_VmPub->top = this->stack_; - native::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1]; + native::scr_VmPub->maxStack = &this->stack_[ARRAYSIZE(this->stack_) - 1]; native::scr_VmPub->inparamcount = 0; native::scr_VmPub->outparamcount = 0; } @@ -22,6 +22,6 @@ namespace game::scripting native::scr_VmPub->inparamcount = this->in_param_count_; native::scr_VmPub->outparamcount = this->out_param_count_; native::scr_VmPub->top = this->top_; - native::scr_VmPub->maxstack = this->max_stack_; + native::scr_VmPub->maxStack = this->max_stack_; } } diff --git a/src/game/structs.hpp b/src/game/structs.hpp index d583e6e..e3be37d 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -463,14 +463,18 @@ namespace game typedef void (__cdecl * scr_call_t)(int entref); + typedef unsigned __int16 scr_string_t; + enum scriptType_e { SCRIPT_NONE = 0, SCRIPT_OBJECT = 1, SCRIPT_STRING = 2, + SCRIPT_ISTRING = 2, SCRIPT_VECTOR = 4, SCRIPT_FLOAT = 5, SCRIPT_INTEGER = 6, + SCRIPT_CODEPOS = 7, SCRIPT_END = 8, // Custom }; @@ -521,7 +525,7 @@ namespace game struct scrVmPub_t { unsigned int* localVars; - VariableValue* maxstack; + VariableValue* maxStack; int function_count; function_frame_t* function_frame; VariableValue* top; @@ -537,6 +541,52 @@ namespace game VariableValue stack[2048]; }; + struct HunkUser + { + HunkUser* current; + HunkUser* next; + int maxSize; + int end; + int pos; + const char* name; + bool fixed; + int type; + unsigned char buf[1]; + }; + + static_assert(sizeof(HunkUser) == 0x24); + + struct scrVarPub_t + { + const char* fieldBuffer; + bool evaluate; + unsigned int time; + unsigned int timeArrayId; + unsigned int pauseArrayId; + unsigned int notifyArrayId; + unsigned int objectStackId; + unsigned int levelId; + unsigned int gameId; + unsigned int animId; + unsigned int freeEntList; + unsigned int tempVariable; + unsigned int numScriptValues[2]; + bool bInited; + bool abort; + unsigned __int16 savecount; + unsigned __int16 savecountMark; + unsigned int entId; + unsigned int entFieldName; + unsigned int checksum; + HunkUser* programHunkUser; + HunkUser* canonicalStringHunkUser; + const char* programBuffer; + unsigned __int16 saveIdMap[36864]; + unsigned __int16 saveIdMapRev[36864]; + }; + + static_assert(sizeof(scrVarPub_t) == 0x24058); + struct scr_classStruct_t { unsigned __int16 id; @@ -974,7 +1024,7 @@ namespace game int maxHealth; int damage; int count; - unsigned char __pad2[0xc8]; + unsigned char __pad2[0xC8]; }; static_assert(sizeof(gentity_s) == 0x274); diff --git a/src/module/chat.cpp b/src/module/chat.cpp new file mode 100644 index 0000000..2572cdb --- /dev/null +++ b/src/module/chat.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include "game/game.hpp" + +#include + +static void notify_on_say(game::native::gentity_s* ent, int mode, const char* message) +{ + 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_NotifyLevel(game::native::SL_GetString("say", 0), 3); +} + +static __declspec(naked) void g_say_stub() +{ + __asm + { + pushad + + push [esp + 0x20 + 0x108] // message + push [esp + 0x20 + 0x108] // mode + push ebp // ent + call notify_on_say + add esp, 0xC + + popad + + push 0x5C2940 // I_strncpyz + retn + } +} + +class chat final : public module +{ +public: + void post_load() override + { + if (game::is_mp()) + { + utils::hook(0x502BAF, &g_say_stub, HOOK_CALL).install()->quick(); // I_strncpyz + } + } +}; + +REGISTER_MODULE(chat) diff --git a/src/module/test_clients.cpp b/src/module/test_clients.cpp index 0e08ceb..9ccae59 100644 --- a/src/module/test_clients.cpp +++ b/src/module/test_clients.cpp @@ -61,7 +61,7 @@ game::native::gentity_s* test_clients::sv_add_test_client() game::native::SV_Cmd_EndTokenizedString(); // Find the bot - auto idx = 1; + int idx; for (idx = 0; idx < *game::native::svs_clientCount; idx++) { if (game::native::mp::svs_clients[idx].header.state == game::native::clientState_t::CS_FREE) @@ -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); + reinterpret_cast(0x569E30)(sys); } __declspec(naked) void test_clients::reset_reliable_mp()