676 lines
17 KiB
C++
Raw Normal View History

2022-02-27 12:53:44 +00:00
#include <STDInclude.hpp>
#include "Script.hpp"
2017-01-19 22:23:59 +01:00
namespace Components
{
std::unordered_map<std::string, Script::ScriptFunction> Script::CustomScrFunctions;
std::unordered_map<std::string, Script::ScriptMethod> Script::CustomScrMethods;
2017-01-19 22:23:59 +01:00
std::string Script::ScriptName;
std::vector<std::string> Script::ScriptNameStack;
unsigned short Script::FunctionName;
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
2022-06-25 14:22:13 +02:00
int Script::LastFrameTime = -1;
2021-11-13 13:15:27 +00:00
std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
const char* Script::ReplacedPos = nullptr;
2022-06-25 14:22:13 +02:00
std::vector<int> Script::ScriptMainHandles;
std::vector<int> Script::ScriptInitHandles;
2017-01-19 22:23:59 +01:00
void Script::FunctionError()
{
2022-04-12 14:34:51 +02:00
const auto* funcName = Game::SL_ConvertToString(Script::FunctionName);
2017-01-19 22:23:59 +01:00
Game::Scr_ShutdownAllocNode();
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: unknown function {} in {}\n", funcName, Script::ScriptName);
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n");
2017-01-19 22:23:59 +01:00
2022-06-12 23:07:53 +02:00
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function {}\n{}\n\n", funcName, Script::ScriptName);
2017-01-19 22:23:59 +01:00
}
__declspec(naked) void Script::StoreFunctionNameStub()
{
__asm
{
mov eax, [esp - 8h]
mov Script::FunctionName, ax
sub esp, 0Ch
push 0
push edi
mov eax, 612DB6h
jmp eax
}
}
2022-01-16 17:25:51 +00:00
void Script::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage)
{
const auto developer = Dvar::Var("developer").get<int>();
// Allow error messages to be printed if developer mode is on
2022-01-23 19:32:20 +00:00
// Should check scrVarPub.developer but it's absent
// in this version of the game so let's check the dvar
2022-01-16 17:25:51 +00:00
if (!Game::scrVmPub->terminal_error && !developer)
return;
2022-01-23 19:32:20 +00:00
// If were are developing let's call RuntimeErrorInternal
2022-01-18 00:21:25 +00:00
// scrVmPub.debugCode seems to be always false
2022-01-16 17:25:51 +00:00
if (Game::scrVmPub->debugCode || Game::scrVarPub->developer_script)
{
2022-06-12 23:07:53 +02:00
Game::RuntimeErrorInternal(Game::CON_CHANNEL_PARSERSCRIPT, codePos, index, msg);
2022-01-16 17:25:51 +00:00
}
else
{
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "{}\n", msg);
2022-01-16 17:25:51 +00:00
}
// Let's not throw error unless we have to
2022-04-09 16:29:58 +02:00
if (Game::scrVmPub->terminal_error)
{
if (dialogMessage == nullptr)
dialogMessage = "";
2022-01-16 17:25:51 +00:00
2022-06-12 23:07:53 +02:00
Logger::Error(Game::ERR_SCRIPT_DROP, "\x15script runtime error\n(see console for details)\n{}\n{}", msg, dialogMessage);
2022-04-09 16:29:58 +02:00
}
2022-01-16 17:25:51 +00:00
}
2017-01-19 22:23:59 +01:00
void Script::StoreScriptName(const char* name)
{
Script::ScriptNameStack.push_back(Script::ScriptName);
Script::ScriptName = name;
if (!Utils::String::EndsWith(Script::ScriptName, ".gsc"))
{
Script::ScriptName.append(".gsc");
}
}
__declspec(naked) void Script::StoreScriptNameStub()
{
__asm
{
2017-02-01 13:44:25 +01:00
pushad
lea ecx, [esp + 30h]
2017-01-19 22:23:59 +01:00
push ecx
call Script::StoreScriptName
add esp, 4h
2017-02-01 13:44:25 +01:00
popad
2017-01-19 22:23:59 +01:00
push ebp
mov ebp, ds:1CDEAA8h
2017-02-01 13:44:25 +01:00
push 427DC3h
retn
2017-01-19 22:23:59 +01:00
}
}
void Script::RestoreScriptName()
{
Script::ScriptName = Script::ScriptNameStack.back();
Script::ScriptNameStack.pop_back();
}
__declspec(naked) void Script::RestoreScriptNameStub()
{
__asm
{
2017-02-01 13:44:25 +01:00
pushad
2017-01-19 22:23:59 +01:00
call Script::RestoreScriptName
2017-02-01 13:44:25 +01:00
popad
2017-01-19 22:23:59 +01:00
mov ds:1CDEAA8h, ebp
2017-02-01 13:44:25 +01:00
push 427E77h
retn
2017-01-19 22:23:59 +01:00
}
}
void Script::PrintSourcePos(const char* filename, unsigned int offset)
{
FileSystem::File script(filename);
if (script.exists())
{
std::string buffer = script.getBuffer();
Utils::String::Replace(buffer, "\t", " ");
2022-04-10 14:13:32 +02:00
auto line = 1, lineOffset = 0, inlineOffset = 0;
2017-01-19 22:23:59 +01:00
2022-04-10 14:13:32 +02:00
for (size_t i = 0; i < buffer.size(); ++i)
2017-01-19 22:23:59 +01:00
{
// Terminate line
if (i == offset)
{
while (buffer[i] != '\r' && buffer[i] != '\n' && buffer[i] != '\0')
{
++i;
}
buffer[i] = '\0';
break;
}
if (buffer[i] == '\n')
{
++line;
2022-04-10 14:13:32 +02:00
lineOffset = static_cast<int>(i); // Includes the line break!
2017-01-19 22:23:59 +01:00
inlineOffset = 0;
}
else
{
++inlineOffset;
}
}
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "in file {}, line {}:", filename, line);
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "{}\n", buffer.substr(lineOffset));
2017-01-19 22:23:59 +01:00
2022-04-10 14:13:32 +02:00
for (auto i = 0; i < (inlineOffset - 1); ++i)
2017-01-19 22:23:59 +01:00
{
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, " ");
2017-01-19 22:23:59 +01:00
}
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "*\n");
2017-01-19 22:23:59 +01:00
}
else
{
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "in file {}, offset {}\n", filename, offset);
2017-01-19 22:23:59 +01:00
}
}
void Script::CompileError(unsigned int offset, const char* message, ...)
{
2022-02-01 21:51:17 +01:00
char msgbuf[1024] = {0};
va_list va;
va_start(va, message);
_vsnprintf_s(msgbuf, _TRUNCATE, message, va);
2022-02-01 21:51:17 +01:00
va_end(va);
2017-01-19 22:23:59 +01:00
Game::Scr_ShutdownAllocNode();
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: {} ", msgbuf);
2017-01-19 22:23:59 +01:00
Script::PrintSourcePos(Script::ScriptName.data(), offset);
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n\n");
2017-01-19 22:23:59 +01:00
2022-06-12 23:07:53 +02:00
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\n{}\n{}\n(see console for actual details)\n", msgbuf, Script::ScriptName);
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
void Script::Scr_LoadGameType_Stub()
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
for (const auto& handle : Script::ScriptMainHandles)
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
const auto id = Game::Scr_ExecThread(handle, 0);
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
Game::Scr_LoadGameType();
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
void Script::Scr_StartupGameType_Stub()
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
for (const auto& handle : Script::ScriptInitHandles)
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
const auto id = Game::Scr_ExecThread(handle, 0);
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
Game::Scr_StartupGameType();
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
void Script::GScr_LoadGameTypeScript_Stub()
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
// Clear handles (from previous GSC loading session)
Script::ScriptMainHandles.clear();
Script::ScriptInitHandles.clear();
2017-01-19 22:23:59 +01:00
const auto list = FileSystem::GetFileList("scripts/", "gsc");
2017-01-19 22:23:59 +01:00
2022-06-25 14:22:13 +02:00
for (const auto& file : list)
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
std::string script = "scripts/" + file;
2017-01-19 22:23:59 +01:00
2022-06-25 14:22:13 +02:00
if (Utils::String::EndsWith(script, ".gsc"))
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
script = script.substr(0, script.size() - 4);
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
Logger::Print("Loading script {}.gsc...\n", script);
2022-06-25 14:22:13 +02:00
if (!Game::Scr_LoadScript(script.data()))
{
2022-06-25 14:22:13 +02:00
Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", script);
Logger::Error(Game::ERR_DROP, "Could not find script '{}'", script);
return;
}
2022-06-25 14:22:13 +02:00
Logger::Print("Script {}.gsc loaded successfully.\n", script);
Logger::Debug("Finding script handle main or init...");
const auto initHandle = Game::Scr_GetFunctionHandle(script.data(), "init");
if (initHandle != 0)
2017-01-19 22:23:59 +01:00
{
2022-06-25 14:22:13 +02:00
Script::ScriptInitHandles.push_back(initHandle);
2017-01-19 22:23:59 +01:00
}
2022-06-25 14:22:13 +02:00
const auto mainHandle = Game::Scr_GetFunctionHandle(script.data(), "main");
if (mainHandle != 0)
{
Script::ScriptMainHandles.push_back(mainHandle);
}
// Allow scripts with no handles
2017-01-19 22:23:59 +01:00
}
Game::GScr_LoadGameTypeScript();
}
void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type)
{
Script::ScriptFunction toAdd;
toAdd.actionFunc = func;
toAdd.type = type;
2022-07-23 23:22:58 +02:00
CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd);
}
void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type)
{
Script::ScriptMethod toAdd;
toAdd.actionFunc = func;
toAdd.type = type;
2022-07-23 23:22:58 +02:00
CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd);
}
2022-04-12 14:34:51 +02:00
Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type)
2017-05-14 20:14:52 +02:00
{
2022-04-10 14:13:32 +02:00
if (pName != nullptr)
{
2022-07-23 23:22:58 +02:00
const auto got = Script::CustomScrFunctions.find(Utils::String::ToLower(*pName));
// If no function was found let's call game's function
if (got != Script::CustomScrFunctions.end())
{
*type = got->second.type;
return got->second.actionFunc;
}
}
2022-04-10 14:00:55 +02:00
else
{
for (const auto& [name, builtin] : Script::CustomScrFunctions)
{
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data());
}
}
2022-04-12 14:34:51 +02:00
return Utils::Hook::Call<Game::BuiltinFunction(const char**, int*)>(0x5FA2B0)(pName, type); // BuiltIn_GetFunction
}
Game::BuiltinMethod Script::BuiltIn_GetMethodStub(const char** pName, int* type)
{
2022-04-10 14:13:32 +02:00
if (pName != nullptr)
{
2022-07-23 23:22:58 +02:00
const auto got = Script::CustomScrMethods.find(Utils::String::ToLower(*pName));
// If no method was found let's call game's function
if (got != Script::CustomScrMethods.end())
{
2022-04-10 14:00:55 +02:00
*type = got->second.type;
return got->second.actionFunc;
}
}
2022-04-10 14:00:55 +02:00
else
{
for (const auto& [name, builtin] : Script::CustomScrMethods)
{
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data());
}
}
2022-04-12 14:34:51 +02:00
return Utils::Hook::Call<Game::BuiltinMethod(const char**, int*)>(0x5FA360)(pName, type); // Player_GetMethod
}
void Script::StoreScriptBaseProgramNum()
{
Script::ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get<int>(0x1CFEEF8), Script::ScriptName);
}
void Script::Scr_PrintPrevCodePos(int scriptPos)
{
2022-02-04 15:08:43 +01:00
auto bestCodePos = -1, nextCodePos = -1, offset = -1;
std::string file;
2022-02-01 21:51:17 +01:00
for (const auto& [key, value] : Script::ScriptBaseProgramNum)
{
const auto codePos = key;
if (codePos > scriptPos)
{
if (nextCodePos == -1 || codePos < nextCodePos)
nextCodePos = codePos;
continue;
}
if (codePos < bestCodePos)
continue;
bestCodePos = codePos;
2022-02-01 21:51:17 +01:00
file = value;
offset = scriptPos - bestCodePos;
}
if (bestCodePos == -1)
return;
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n@ {} ({} - {})\n", scriptPos, bestCodePos, nextCodePos);
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "in {} ({} through the source)\n\n", file, ((offset * 100.0f) / (nextCodePos - bestCodePos)));
}
__declspec(naked) void Script::Scr_PrintPrevCodePosStub()
{
__asm
{
push esi
call Script::Scr_PrintPrevCodePos
add esp, 4h
2017-05-14 20:14:52 +02:00
pop esi
retn
}
}
__declspec(naked) void Script::StoreScriptBaseProgramNumStub()
{
__asm
{
// execute our hook
pushad
call Script::StoreScriptBaseProgramNum
popad
// execute overwritten code caused by the jump hook
sub eax, ds:201A460h // gScrVarPub_programBuffer
add esp, 0Ch
mov ds : 1CFEEF8h, eax // gScrCompilePub_programLen
// jump back to the original code
push 426C3Bh
retn
}
}
2021-11-13 13:22:06 +00:00
unsigned int Script::SetExpFogStub()
2017-04-29 23:08:41 +02:00
{
if (Game::Scr_GetNumParam() == 6)
2017-04-29 23:08:41 +02:00
{
std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6);
Game::scrVmPub->top += 1;
Game::scrVmPub->top[-6].type = Game::VAR_FLOAT;
Game::scrVmPub->top[-6].u.floatValue = 0.0f;
2017-05-07 19:04:57 +02:00
++Game::scrVmPub->outparamcount;
2017-04-29 23:08:41 +02:00
}
return Game::Scr_GetNumParam();
}
const char* Script::GetCodePosForParam(int index)
2021-11-13 13:15:27 +00:00
{
2021-11-13 20:55:22 +00:00
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount)
{
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Index is out of range!\n");
return "";
}
2022-07-21 18:56:16 +02:00
const auto* value = &Game::scrVmPub->top[-index];
2022-05-04 12:44:45 +01:00
if (value->type != Game::scrParamType_t::VAR_FUNCTION)
{
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Expects a function as parameter!\n");
return "";
}
return value->u.codePosValue;
2021-11-13 13:15:27 +00:00
}
void Script::GetReplacedPos(const char* pos)
{
2022-05-31 18:38:09 +02:00
if (Script::ReplacedFunctions.contains(pos))
2021-11-13 13:15:27 +00:00
{
2021-11-13 13:22:06 +00:00
Script::ReplacedPos = Script::ReplacedFunctions[pos];
2021-11-13 13:15:27 +00:00
}
}
void Script::SetReplacedPos(const char* what, const char* with)
{
if (what[0] == '\0' || with[0] == '\0')
{
2022-06-12 23:07:53 +02:00
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "Invalid parameters passed to ReplacedFunctions\n");
return;
}
2022-05-31 18:38:09 +02:00
if (Script::ReplacedFunctions.contains(what))
2021-11-13 13:15:27 +00:00
{
2022-06-12 23:07:53 +02:00
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n");
2021-11-13 13:15:27 +00:00
}
Script::ReplacedFunctions[what] = with;
}
__declspec(naked) void Script::VMExecuteInternalStub()
{
__asm
{
pushad
push edx
call Script::GetReplacedPos
pop edx
popad
cmp Script::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
2021-11-13 13:15:27 +00:00
retn
SetPos:
mov edx, Script::ReplacedPos
mov Script::ReplacedPos, 0
movzx eax, byte ptr [edx]
inc edx
jmp Loc1
}
}
Game::client_t* Script::GetClient(const Game::gentity_t* ent)
{
assert(ent != nullptr);
if (ent->client == nullptr)
{
Game::Scr_ObjectError(Utils::String::VA("Entity %i is not a player", ent->s.number));
2022-01-07 22:00:44 +01:00
return nullptr;
}
2022-01-07 22:00:44 +01:00
2022-04-12 14:34:51 +02:00
if (ent->s.number >= *Game::svs_clientCount)
2022-01-23 19:32:20 +00:00
{
Game::Scr_ObjectError(Utils::String::VA("Entity %i is out of bounds", ent->s.number));
2022-01-23 19:32:20 +00:00
return nullptr;
}
return &Game::svs_clients[ent->s.number];
}
void Script::AddFunctions()
{
2022-07-23 23:22:58 +02:00
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
2021-11-13 13:15:27 +00:00
{
if (Game::Scr_GetNumParam() != 2)
2021-11-13 13:15:27 +00:00
{
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
return;
}
const auto what = Script::GetCodePosForParam(0);
const auto with = Script::GetCodePosForParam(1);
2021-11-13 13:15:27 +00:00
Script::SetReplacedPos(what, with);
});
// System time
2022-07-23 23:22:58 +02:00
Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds()
2020-12-08 16:18:09 -06:00
{
SYSTEMTIME time;
GetSystemTime(&time);
2020-12-08 16:18:09 -06:00
Game::Scr_AddInt(time.wMilliseconds);
});
// Executes command to the console
2022-07-23 23:22:58 +02:00
Script::AddFunction("Exec", [] // gsc: Exec(<string>)
2020-12-08 16:18:09 -06:00
{
2022-01-07 22:00:44 +01:00
const auto str = Game::Scr_GetString(0);
2022-01-23 19:32:20 +00:00
if (str == nullptr)
{
2022-02-01 21:51:17 +01:00
Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n");
2022-01-23 19:32:20 +00:00
return;
}
2020-12-08 16:18:09 -06:00
Command::Execute(str, false);
});
// Allow printing to the console even when developer is 0
2022-07-23 23:22:58 +02:00
Script::AddFunction("PrintConsole", [] // gsc: PrintConsole(<string>)
{
2022-06-12 23:07:53 +02:00
for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i)
{
2022-06-12 23:07:53 +02:00
const auto* str = Game::Scr_GetString(i);
if (str == nullptr)
{
2022-02-01 21:51:17 +01:00
Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n");
return;
}
2022-06-12 23:07:53 +02:00
Logger::Print(Game::level->scriptPrintChannel, "{}", str);
}
});
2022-01-15 13:49:33 +00:00
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
2022-07-23 23:22:58 +02:00
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
{
const auto* ent = Game::GetPlayerEntity(entref);
Game::Scr_AddBool((ent->client->flags & Game::PLAYER_FLAG_FROZEN) != 0);
});
}
2017-01-19 22:23:59 +01:00
Script::Script()
{
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
2022-01-16 17:25:51 +00:00
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();
2022-02-09 23:40:27 +00:00
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
// On IW5 the function is entirely nullsubbed
Utils::Hook::Set<BYTE>(0x5F8DBF, 0xEB);
2017-01-19 22:23:59 +01:00
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick();
2022-06-25 14:22:13 +02:00
Utils::Hook(0x48EFFE, Script::Scr_LoadGameType_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x48F008, Script::Scr_StartupGameType_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x45D44A, Script::GScr_LoadGameTypeScript_Stub, HOOK_CALL).install()->quick();
2017-04-29 23:08:41 +02:00
// Fetch custom functions
Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction
Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod
2017-04-29 23:08:41 +02:00
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
2017-05-14 20:14:52 +02:00
2021-11-13 13:15:27 +00:00
Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1);
Scheduler::Loop([]()
2020-12-19 16:50:51 -06:00
{
if (!Game::SV_Loaded())
return;
const auto nowMs = Game::Sys_Milliseconds();
2020-12-19 16:50:51 -06:00
if (Script::LastFrameTime != -1)
{
const auto timeScale = Dvar::Var("timescale").get<float>();
const auto timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * timeScale);
2020-12-19 16:50:51 -06:00
if (timeTaken >= 500)
2022-06-12 23:07:53 +02:00
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Hitch warning: {} msec frame time\n", timeTaken);
2020-12-19 16:50:51 -06:00
}
Script::LastFrameTime = nowMs;
}, Scheduler::Pipeline::SERVER);
2020-12-19 16:50:51 -06:00
2022-04-09 16:29:58 +02:00
#ifdef _DEBUG
2022-05-04 12:44:45 +01:00
Script::AddFunction("DebugBox", []
2020-12-08 16:18:09 -06:00
{
const auto* message = Game::Scr_GetString(0);
if (message == nullptr)
{
Game::Scr_Error("^1DebugBox: Illegal parameter!\n");
}
MessageBoxA(nullptr, message, "DEBUG", MB_OK);
2022-02-25 11:58:11 +00:00
}, 1);
2022-04-09 16:29:58 +02:00
#endif
Script::AddFunctions();
Events::OnVMShutdown([]
{
Script::ReplacedFunctions.clear();
});
2017-01-19 22:23:59 +01:00
}
}