From c0e4a5b4ac00009a8d37b2074db814306e94e13d Mon Sep 17 00:00:00 2001 From: Edo Date: Fri, 24 Feb 2023 11:23:06 +0100 Subject: [PATCH] [Script]: Re-organize some functions (#795) --- deps/premake/mongoose.lua | 6 +- src/Components/Modules/Chat.cpp | 3 +- src/Components/Modules/GSC/Script.cpp | 180 ------------------ src/Components/Modules/GSC/Script.hpp | 11 -- .../Modules/GSC/ScriptExtension.cpp | 157 +++++++++++++++ .../Modules/GSC/ScriptExtension.hpp | 9 + 6 files changed, 172 insertions(+), 194 deletions(-) diff --git a/deps/premake/mongoose.lua b/deps/premake/mongoose.lua index edea2692..aa29a89e 100644 --- a/deps/premake/mongoose.lua +++ b/deps/premake/mongoose.lua @@ -3,13 +3,15 @@ mongoose = { } function mongoose.import() - links {"mongoose"} + links "mongoose" mongoose.includes() end function mongoose.includes() - includedirs {mongoose.source} + includedirs { + mongoose.source, + } end function mongoose.project() diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index d0c4c3e8..6c075875 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -4,6 +4,7 @@ #include "Voice.hpp" #include "GSC/Script.hpp" +#include "GSC/ScriptExtension.hpp" namespace Components { @@ -599,7 +600,7 @@ namespace Components return; } - const auto* func = Script::GetCodePosForParam(0); + const auto* func = ScriptExtension::GetCodePosForParam(0); SayCallbacks.emplace_back(func); }); } diff --git a/src/Components/Modules/GSC/Script.cpp b/src/Components/Modules/GSC/Script.cpp index bcec4b8f..561d1717 100644 --- a/src/Components/Modules/GSC/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -6,9 +6,6 @@ namespace Components std::vector Script::CustomScrFunctions; std::vector Script::CustomScrMethods; - std::unordered_map Script::ReplacedFunctions; - const char* Script::ReplacedPos = nullptr; - std::unordered_map Script::ScriptMainHandles; std::unordered_map Script::ScriptInitHandles; @@ -204,94 +201,6 @@ namespace Components return Game::Scr_GetNumParam(); } - const char* Script::GetCodePosForParam(int index) - { - if (static_cast(index) >= Game::scrVmPub->outparamcount) - { - Game::Scr_ParamError(static_cast(index), "^1GetCodePosForParam: Index is out of range!\n"); - return ""; - } - - const auto* value = &Game::scrVmPub->top[-index]; - - if (value->type != Game::VAR_FUNCTION) - { - Game::Scr_ParamError(static_cast(index), "^1GetCodePosForParam: Expects a function as parameter!\n"); - return ""; - } - - return value->u.codePosValue; - } - - void Script::GetReplacedPos(const char* pos) - { - if (ReplacedFunctions.contains(pos)) - { - ReplacedPos = ReplacedFunctions[pos]; - } - } - - void Script::SetReplacedPos(const char* what, const char* with) - { - if (what[0] == '\0' || with[0] == '\0') - { - Logger::Warning(Game::CON_CHANNEL_SCRIPT, "Invalid parameters passed to ReplacedFunctions\n"); - return; - } - - if (ReplacedFunctions.contains(what)) - { - Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n"); - } - - ReplacedFunctions[what] = with; - } - - __declspec(naked) void Script::VMExecuteInternalStub() - { - __asm - { - pushad - - push edx - call GetReplacedPos - - pop edx - popad - - cmp ReplacedPos, 0 - jne SetPos - - movzx eax, byte ptr [edx] - inc edx - - Loc1: - cmp eax, 0x8B - - push ecx - - mov ecx, 0x2045094 - mov [ecx], eax - - mov ecx, 0x2040CD4 - mov [ecx], edx - - pop ecx - - push 0x61E944 - retn - - SetPos: - mov edx, ReplacedPos - mov ReplacedPos, 0 - - movzx eax, byte ptr [edx] - inc edx - - jmp Loc1 - } - } - Game::client_t* Script::GetClient(const Game::gentity_t* ent) { assert(ent); @@ -311,71 +220,6 @@ namespace Components return &Game::svs_clients[ent->s.number]; } - void Script::AddFunctions() - { - AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) - { - if (Game::Scr_GetNumParam() != 2) - { - Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n"); - return; - } - - const auto what = GetCodePosForParam(0); - const auto with = GetCodePosForParam(1); - - SetReplacedPos(what, with); - }); - - // System time - AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds() - { - SYSTEMTIME time; - GetSystemTime(&time); - - Game::Scr_AddInt(time.wMilliseconds); - }); - - // Executes command to the console - AddFunction("Exec", [] // gsc: Exec() - { - const auto str = Game::Scr_GetString(0); - - if (str == nullptr) - { - Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n"); - return; - } - - Command::Execute(str, false); - }); - - // Allow printing to the console even when developer is 0 - AddFunction("PrintConsole", [] // gsc: PrintConsole() - { - for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i) - { - const auto* str = Game::Scr_GetString(i); - - if (str == nullptr) - { - Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n"); - return; - } - - Logger::Print(Game::level->scriptPrintChannel, "{}", str); - } - }); - - // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 - AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); - { - const auto* ent = Scr_GetPlayerEntity(entref); - - Game::Scr_AddBool((ent->client->flags & Game::PF_FROZEN) != 0); - }); - } - Script::Script() { // Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError. @@ -391,29 +235,5 @@ namespace Components Utils::Hook(0x4EC8DD, BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod Utils::Hook(0x5F41A3, SetExpFogStub, HOOK_CALL).install()->quick(); - - Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick(); - Utils::Hook::Nop(0x61E933, 1); - -#ifdef _DEBUG - AddFunction("DebugBox", [] - { - const auto* message = Game::Scr_GetString(0); - - if (message == nullptr) - { - Game::Scr_Error("^1DebugBox: Illegal parameter!\n"); - } - - MessageBoxA(nullptr, message, "DEBUG", MB_OK); - }, true); -#endif - - AddFunctions(); - - Events::OnVMShutdown([] - { - ReplacedFunctions.clear(); - }); } } diff --git a/src/Components/Modules/GSC/Script.hpp b/src/Components/Modules/GSC/Script.hpp index bc63f525..ee7699f1 100644 --- a/src/Components/Modules/GSC/Script.hpp +++ b/src/Components/Modules/GSC/Script.hpp @@ -16,8 +16,6 @@ namespace Components static Game::client_t* GetClient(const Game::gentity_t* gentity); - static const char* GetCodePosForParam(int index); - // Probably a macro 'originally' but this is fine static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref) { @@ -60,9 +58,6 @@ namespace Components static std::unordered_map ScriptMainHandles; static std::unordered_map ScriptInitHandles; - static std::unordered_map ReplacedFunctions; - static const char* ReplacedPos; - static void Scr_LoadGameType_Stub(); static void Scr_StartupGameType_Stub(); static void GScr_LoadGameTypeScript_Stub(); @@ -71,11 +66,5 @@ namespace Components static Game::BuiltinMethod BuiltIn_GetMethodStub(const char** pName, int* type); static unsigned int SetExpFogStub(); - - static void GetReplacedPos(const char* pos); - static void SetReplacedPos(const char* what, const char* with); - static void VMExecuteInternalStub(); - - static void AddFunctions(); }; } diff --git a/src/Components/Modules/GSC/ScriptExtension.cpp b/src/Components/Modules/GSC/ScriptExtension.cpp index 0b30a4f0..cc166fc2 100644 --- a/src/Components/Modules/GSC/ScriptExtension.cpp +++ b/src/Components/Modules/GSC/ScriptExtension.cpp @@ -7,6 +7,9 @@ namespace Components std::unordered_map ScriptExtension::CustomEntityFields; std::unordered_map ScriptExtension::CustomClientFields; + std::unordered_map ScriptExtension::ReplacedFunctions; + const char* ScriptExtension::ReplacedPos = nullptr; + void ScriptExtension::AddEntityField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter) { @@ -106,6 +109,94 @@ namespace Components Game::Scr_GetEntityField(entnum, offset); } + const char* ScriptExtension::GetCodePosForParam(int index) + { + if (static_cast(index) >= Game::scrVmPub->outparamcount) + { + Game::Scr_ParamError(static_cast(index), "^1GetCodePosForParam: Index is out of range!\n"); + return ""; + } + + const auto* value = &Game::scrVmPub->top[-index]; + + if (value->type != Game::VAR_FUNCTION) + { + Game::Scr_ParamError(static_cast(index), "^1GetCodePosForParam: Expects a function as parameter!\n"); + return ""; + } + + return value->u.codePosValue; + } + + void ScriptExtension::GetReplacedPos(const char* pos) + { + if (ReplacedFunctions.contains(pos)) + { + ReplacedPos = ReplacedFunctions[pos]; + } + } + + void ScriptExtension::SetReplacedPos(const char* what, const char* with) + { + if (!*what || !*with) + { + Logger::Warning(Game::CON_CHANNEL_SCRIPT, "Invalid parameters passed to ReplacedFunctions\n"); + return; + } + + if (ReplacedFunctions.contains(what)) + { + Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n"); + } + + ReplacedFunctions[what] = with; + } + + __declspec(naked) void ScriptExtension::VMExecuteInternalStub() + { + __asm + { + pushad + + push edx + call GetReplacedPos + + pop edx + popad + + cmp ReplacedPos, 0 + jne SetPos + + movzx eax, byte ptr [edx] + inc edx + + Loc1: + cmp eax, 0x8B + + push ecx + + mov ecx, 0x2045094 + mov [ecx], eax + + mov ecx, 0x2040CD4 + mov [ecx], edx + + pop ecx + + push 0x61E944 + ret + + SetPos: + mov edx, ReplacedPos + mov ReplacedPos, 0 + + movzx eax, byte ptr [edx] + inc edx + + jmp Loc1 + } + } + void ScriptExtension::AddFunctions() { // Misc functions @@ -234,6 +325,57 @@ namespace Components Game::Scr_AddInt(result); }); + + Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) + { + if (Game::Scr_GetNumParam() != 2) + { + Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n"); + return; + } + + const auto what = GetCodePosForParam(0); + const auto with = GetCodePosForParam(1); + + SetReplacedPos(what, with); + }); + + + Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds() + { + SYSTEMTIME time; + GetSystemTime(&time); + + Game::Scr_AddInt(time.wMilliseconds); + }); + + Script::AddFunction("Exec", [] // gsc: Exec() + { + const auto* str = Game::Scr_GetString(0); + if (!str) + { + Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n"); + return; + } + + Command::Execute(str, false); + }); + + // Allow printing to the console even when developer is 0 + Script::AddFunction("PrintConsole", [] // gsc: PrintConsole() + { + for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i) + { + const auto* str = Game::Scr_GetString(i); + if (!str) + { + Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n"); + return; + } + + Logger::Print(Game::level->scriptPrintChannel, "{}", str); + } + }); } void ScriptExtension::AddMethods() @@ -271,6 +413,13 @@ namespace Components client->ping = ping; }); + + // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 + Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); + { + const auto* ent = Script::Scr_GetPlayerEntity(entref); + Game::Scr_AddBool((ent->client->flags & Game::PF_FROZEN) != 0); + }); } void ScriptExtension::AddEntityFields() @@ -311,5 +460,13 @@ namespace Components Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField + + Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick(); + Utils::Hook::Nop(0x61E933, 1); + + Events::OnVMShutdown([] + { + ReplacedFunctions.clear(); + }); } } diff --git a/src/Components/Modules/GSC/ScriptExtension.hpp b/src/Components/Modules/GSC/ScriptExtension.hpp index c153f24c..5f8c6fcc 100644 --- a/src/Components/Modules/GSC/ScriptExtension.hpp +++ b/src/Components/Modules/GSC/ScriptExtension.hpp @@ -10,10 +10,15 @@ namespace Components static void AddEntityField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter); static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter); + static const char* GetCodePosForParam(int index); + private: static std::unordered_map CustomEntityFields; static std::unordered_map CustomClientFields; + static std::unordered_map ReplacedFunctions; + static const char* ReplacedPos; + static void GScr_AddFieldsForEntityStub(); // Two hooks because it makes our code cleaner (luckily functions were not inlined) @@ -23,6 +28,10 @@ namespace Components // One hook because functions were inlined static void Scr_GetEntityFieldStub(int entnum, int offset); + static void GetReplacedPos(const char* pos); + static void SetReplacedPos(const char* what, const char* with); + static void VMExecuteInternalStub(); + static void AddFunctions(); static void AddMethods(); static void AddEntityFields();