diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 9581a64c..76e2d1a2 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -45,6 +45,39 @@ namespace Components } } + void Script::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage) + { + const auto developer = Dvar::Var("developer").get(); + + // Allow error messages to be printed if developer mode is on + // Should check scrVarPub.developer but it's absent in this version of the game + if (!Game::scrVmPub->terminal_error && !developer) + return; + + // If were are developing let's just print a brief message + // scrVmPub->debugCode seems to be always false + if (Game::scrVmPub->debugCode || Game::scrVarPub->developer_script) + { + Logger::Print(23, "%s\n", msg); + + if (!Game::scrVmPub->terminal_error) + return; + } + else + { + Game::RuntimeErrorInternal(23, codePos, index, msg); + // Let's not throw error unless we have to + if (!Game::scrVmPub->abort_on_error && !Game::scrVmPub->terminal_error) + return; + } + + if (dialogMessage == nullptr) + dialogMessage = ""; + + const auto errorLevel = (Game::scrVmPub->terminal_error) ? Game::ERR_SCRIPT_DROP : Game::ERR_SCRIPT; + Logger::Error(errorLevel, "\x15script runtime error\n(see console for details)\n%s\n%s", msg, dialogMessage); + } + void Script::StoreScriptName(const char* name) { Script::ScriptNameStack.push_back(Script::ScriptName); @@ -629,20 +662,9 @@ namespace Components Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick(); - // Enable scr_error printing if in developer - Dvar::OnInit([]() - { - const auto developer = Dvar::Var("developer").get(); - const auto developer_script = Dvar::Var("developer_script").get(); - - if (developer > 0) - Utils::Hook::Set(0x48D8C7, 0x75); - - // Seems to always be false, if set to true - // it will not call Com_Error (Useful for debugging) - if (developer_script) - Game::scrVmPub->debugCode = true; - }); + Utils::Hook(0x61E3AD, Script::RuntimeError, HOOK_CALL).install()->quick(); + Utils::Hook(0x621976, Script::RuntimeError, HOOK_CALL).install()->quick(); + Utils::Hook(0x62246E, Script::RuntimeError, HOOK_CALL).install()->quick(); Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick(); Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/Script.hpp index de3c219b..de739dd7 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/Script.hpp @@ -51,6 +51,7 @@ namespace Components static void FunctionError(); static void StoreFunctionNameStub(); + static void RuntimeError(const char*, unsigned int, const char*, const char*); static void StoreScriptName(const char* name); static void StoreScriptNameStub(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index e2337aaa..f507189a 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -468,6 +468,7 @@ namespace Game unsigned short* db_hashTable = reinterpret_cast(0x12412B0); scrVmPub_t* scrVmPub = reinterpret_cast(0x2040CF0); + scrVarPub_t* scrVarPub = reinterpret_cast(0x201A408); clientstate_t* clcState = reinterpret_cast(0xB2C540); @@ -705,6 +706,28 @@ namespace Game Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data())); } + void IncInParam() + { + Scr_ClearOutParams(); + + if (scrVmPub->top == scrVmPub->maxStack) + { + Sys_Error("Internal script stack overflow"); + } + + scrVmPub->top++; + scrVmPub->inparamcount++; + } + + void Scr_AddBool(int value) + { + assert(value == 0 || value == 1); + + IncInParam(); + scrVmPub->top->type = VAR_INTEGER; + scrVmPub->top->u.intValue = value; + } + int FS_FOpenFileReadCurrentThread(const char* file, int* fh) { if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE7FC)) @@ -1019,28 +1042,6 @@ namespace Game return GraphGetValueFromFraction(graph->knotCount, graph->knots, fraction) * graph->scale; } - void IncInParam() - { - Scr_ClearOutParams(); - - if (scrVmPub->top == scrVmPub->maxStack) - { - Sys_Error("Internal script stack overflow"); - } - - scrVmPub->top++; - scrVmPub->inparamcount++; - } - - void Scr_AddBool(int value) - { - assert(value == 0 || value == 1); - - IncInParam(); - scrVmPub->top->type = VAR_INTEGER; - scrVmPub->top->u.intValue = value; - } - #pragma optimize("", off) __declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/) { @@ -1211,6 +1212,27 @@ namespace Game } } + __declspec(naked) void RuntimeErrorInternal(int /*channel*/, const char* /*codePos*/, unsigned int /*index*/, const char* /*msg*/) + { + __asm + { + pushad + + mov eax, [esp + 0x10 + 0x20] + mov edi, [esp + 0x4 + 0x20] + + push [esp + 0xC + 0x20] + push [esp + 0xC + 0x20] + + mov edx, 0x61ABE0 + call edx + add esp, 0x8 + + popad + ret + } + } + __declspec(naked) void IN_KeyUp(kbutton_t* /*button*/) { __asm diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 02096537..79a2bc8d 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -991,6 +991,7 @@ namespace Game extern unsigned short* db_hashTable; extern scrVmPub_t* scrVmPub; + extern scrVarPub_t* scrVarPub; extern clientstate_t* clcState; @@ -1053,6 +1054,7 @@ namespace Game void SV_KickClient(client_t* client, const char* reason); void SV_KickClientError(client_t* client, const std::string& reason); + void RuntimeErrorInternal(int channel, const char* codePos, unsigned int index, const char* msg); void IncInParam(); void Scr_iPrintLn(int clientNum, const std::string& message); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 28537005..bdba2067 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4959,6 +4959,15 @@ namespace Game VariableValue stack[2048]; }; + struct scrVarPub_t + { + const char* fieldBuffer; + unsigned __int16 canonicalStrCount; + bool developer_script; + bool evaluate; + const char* error_message; + }; // Incomplete + enum UILocalVarType { UILOCALVAR_INT = 0x0,