diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 332b43c1..21261246 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -117,7 +117,7 @@ namespace Components void Bots::AddMethods() { - Script::AddFunction("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing() + Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing() { const auto ping = Game::Scr_GetInt(0); @@ -139,7 +139,7 @@ namespace Components client->ping = static_cast(ping); }); - Script::AddFunction("IsTestClient", [](Game::scr_entref_t entref) // Usage: IsTestClient(); + Script::AddMethod("IsTestClient", [](Game::scr_entref_t entref) // Usage: IsTestClient(); { const auto* gentity = Script::GetEntity(entref); const auto* client = Script::GetClient(gentity); @@ -147,7 +147,7 @@ namespace Components Game::Scr_AddBool(client->bIsTestClient == 1); }); - Script::AddFunction("BotStop", [](Game::scr_entref_t entref) // Usage: BotStop(); + Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: BotStop(); { const auto* gentity = Script::GetEntity(entref); const auto* client = Script::GetClient(gentity); @@ -163,7 +163,7 @@ namespace Components g_botai[entref.entnum].active = false; }); - Script::AddFunction("BotWeapon", [](Game::scr_entref_t entref) // Usage: BotWeapon(); + Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: BotWeapon(); { const auto* weapon = Game::Scr_GetString(0); @@ -187,7 +187,7 @@ namespace Components g_botai[entref.entnum].active = true; }); - Script::AddFunction("BotAction", [](Game::scr_entref_t entref) // Usage: BotAction(); + Script::AddMethod("BotAction", [](Game::scr_entref_t entref) // Usage: BotAction(); { const auto* action = Game::Scr_GetString(0); @@ -229,7 +229,7 @@ namespace Components Game::Scr_ParamError(0, "^1BotAction: Unknown action.\n"); }); - Script::AddFunction("BotMovement", [](Game::scr_entref_t entref) // Usage: BotMovement(, ); + Script::AddMethod("BotMovement", [](Game::scr_entref_t entref) // Usage: BotMovement(, ); { auto forwardInt = Game::Scr_GetInt(0); auto rightInt = Game::Scr_GetInt(1); diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 7236951c..5b7136c6 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -175,7 +175,7 @@ namespace Components void ClientCommand::AddScriptFunctions() { - Script::AddFunction("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(); + Script::AddMethod("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(); { const auto* ent = Script::GetEntity(entref); @@ -202,7 +202,7 @@ namespace Components } }); - Script::AddFunction("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(); + Script::AddMethod("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(); { const auto* ent = Script::GetEntity(entref); @@ -229,7 +229,7 @@ namespace Components } }); - Script::AddFunction("God", [](Game::scr_entref_t entref) // gsc: God(); + Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: God(); { auto* ent = Script::GetEntity(entref); @@ -250,7 +250,7 @@ namespace Components } }); - Script::AddFunction("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(); + Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(); { auto* ent = Script::GetEntity(entref); @@ -271,7 +271,7 @@ namespace Components } }); - Script::AddFunction("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(); + Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(); { auto* ent = Script::GetEntity(entref); diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 663cdae7..bcf334bc 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -964,7 +964,7 @@ namespace Components Download::ScriptDownloads.clear(); }); - Script::AddFunction("HttpGet", [](Game::scr_entref_t) + Script::AddFunction("HttpGet", []() { if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return; @@ -984,7 +984,7 @@ namespace Components Game::RemoveRefToObject(object); }); - Script::AddFunction("HttpCancel", [](Game::scr_entref_t) + Script::AddFunction("HttpCancel", []() { if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return; diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 42f401d9..1266ff72 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -4,7 +4,8 @@ namespace Components { std::string Script::ScriptName; std::vector Script::ScriptHandles; - std::vector Script::ScriptFunctions; + std::unordered_map Script::CustomScrFunctions; + std::unordered_map Script::CustomScrMethods; std::vector Script::ScriptNameStack; unsigned short Script::FunctionName; std::unordered_map Script::ScriptStorage; @@ -270,61 +271,77 @@ namespace Components Game::GScr_LoadGameTypeScript(); } - void Script::AddFunction(const std::string& name, Game::scr_function_t function, bool isDev) + void Script::AddFunction(const char* name, Game::xfunction_t func, int type) { - for (auto i = Script::ScriptFunctions.begin(); i != Script::ScriptFunctions.end();) - { - if (i->getName() == name) - { - i = Script::ScriptFunctions.erase(i); - continue; - } + Game::BuiltinFunctionDef toAdd; + toAdd.actionString = name; + toAdd.actionFunc = func; + toAdd.type = type; - ++i; - } - - Script::ScriptFunctions.push_back({ name, function, isDev }); + CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), std::move(toAdd)); } - Game::scr_function_t Script::GetFunction(void* caller, const char** name, int* isDev) + void Script::AddMethod(const char* name, Game::xmethod_t func, int type) { - for (auto& function : Script::ScriptFunctions) - { - if (name && *name) - { - if (Utils::String::ToLower(*name) == Utils::String::ToLower(function.getName())) - { - *name = function.getName(); - *isDev = function.isDev(); - return function.getFunction(); - } - } - else if (caller == reinterpret_cast(0x465781)) - { - Game::Scr_RegisterFunction(function.getFunction()); - } - } + Game::BuiltinMethodDef toAdd; + toAdd.actionString = name; + toAdd.actionFunc = func; + toAdd.type = type; - return nullptr; + CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), std::move(toAdd)); } - __declspec(naked) void Script::GetFunctionStub() + Game::xfunction_t Script::Scr_GetFunctionStub(const char** pName, int* type) { - __asm + for (const auto& [key, value] : Script::CustomScrFunctions) { - test eax, eax - jnz returnSafe - - sub esp, 8h - push [esp + 10h] - call Script::GetFunction - add esp, 0Ch - - returnSafe: - pop edi - pop esi - retn + Game::Scr_RegisterFunction(reinterpret_cast(value.actionFunc), value.actionString); } + + return Utils::Hook::Call(0x44E700)(pName, type); // Scr_GetFunction + } + + Game::xmethod_t Script::Scr_GetMethodStub(const char** pName, int* type) + { + for (const auto& [key, value] : Script::CustomScrMethods) + { + Game::Scr_RegisterFunction(reinterpret_cast(value.actionFunc), value.actionString); + } + + return Utils::Hook::Call(0x4EC870)(pName, type); // Scr_GetMethod + } + + Game::xfunction_t Script::BuiltIn_GetFunctionStub(const char** pName, int* type) + { + if (pName && *pName) + { + const auto got = Script::CustomScrFunctions.find(*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; + } + } + + return Utils::Hook::Call(0x5FA2B0)(pName, type); // BuiltIn_GetFunction + } + + Game::xmethod_t Script::Player_GetMethodStub(const char** pName) + { + if (pName && *pName) + { + const auto got = Script::CustomScrMethods.find(*pName); + + // If no method was found let's call game's function + if (got != Script::CustomScrMethods.end()) + { + return got->second.actionFunc; + } + } + + return Utils::Hook::Call(0x4C07D0)(pName); // Player_GetMethod } void Script::StoreScriptBaseProgramNum() @@ -519,12 +536,14 @@ namespace Components Game::gentity_t* Script::GetEntity(const Game::scr_entref_t entref) { - if (entref.classnum != 0 || entref.entnum >= Game::MAX_GENTITIES) + if (entref.classnum != 0) { Game::Scr_ObjectError("Not an entity"); return nullptr; } + assert(entref.entnum < Game::MAX_GENTITIES); + return &Game::g_entities[entref.entnum]; } @@ -547,7 +566,7 @@ namespace Components void Script::AddFunctions() { - Script::AddFunction("ReplaceFunc", [](Game::scr_entref_t) // gsc: ReplaceFunc(, ) + Script::AddFunction("ReplaceFunc", []() // gsc: ReplaceFunc(, ) { if (Game::Scr_GetNumParam() != 2u) { @@ -562,7 +581,7 @@ namespace Components }); // System time - Script::AddFunction("GetSystemTime", [](Game::scr_entref_t) // gsc: GetSystemTime() + Script::AddFunction("GetSystemTime", []() // gsc: GetSystemTime() { SYSTEMTIME time; GetSystemTime(&time); @@ -570,7 +589,7 @@ namespace Components Game::Scr_AddInt(time.wSecond); }); - Script::AddFunction("GetSystemMilliseconds", [](Game::scr_entref_t) // gsc: GetSystemMilliseconds() + Script::AddFunction("GetSystemMilliseconds", []() // gsc: GetSystemMilliseconds() { SYSTEMTIME time; GetSystemTime(&time); @@ -579,7 +598,7 @@ namespace Components }); // Executes command to the console - Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec() + Script::AddFunction("Exec", []() // gsc: Exec() { const auto str = Game::Scr_GetString(0); @@ -593,7 +612,7 @@ namespace Components }); // Allow printing to the console even when developer is 0 - Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole() + Script::AddFunction("PrintConsole", []() // gsc: PrintConsole() { for (auto i = 0u; i < Game::Scr_GetNumParam(); i++) { @@ -610,7 +629,7 @@ namespace Components }); // Script Storage Funcs - Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(, ); + Script::AddFunction("StorageSet", []() // gsc: StorageSet(, ); { const auto* key = Game::Scr_GetString(0); const auto* value = Game::Scr_GetString(1); @@ -624,7 +643,7 @@ namespace Components Script::ScriptStorage.insert_or_assign(key, value); }); - Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(); + Script::AddFunction("StorageRemove", []() // gsc: StorageRemove(); { const auto* key = Game::Scr_GetString(0); @@ -643,7 +662,7 @@ namespace Components Script::ScriptStorage.erase(key); }); - Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(); + Script::AddFunction("StorageGet", []() // gsc: StorageGet(); { const auto* key = Game::Scr_GetString(0); @@ -663,7 +682,7 @@ namespace Components Game::Scr_AddString(data.data()); }); - Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(); + Script::AddFunction("StorageHas", []() // gsc: StorageHas(); { const auto* key = Game::Scr_GetString(0); @@ -676,13 +695,13 @@ namespace Components Game::Scr_AddBool(Script::ScriptStorage.count(key)); }); - Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear(); + Script::AddFunction("StorageClear", []() // gsc: StorageClear(); { Script::ScriptStorage.clear(); }); // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 - Script::AddFunction("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); + Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); { const auto* ent = Script::GetEntity(entref); @@ -718,8 +737,13 @@ namespace Components Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick(); Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick(); - Utils::Hook(0x44E736, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetFunction - Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod + // Register custom functions + Utils::Hook(0x46577C, Script::Scr_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_BeginLoadScripts + Utils::Hook(0x465787, Script::Scr_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_BeginLoadScripts + + // Fetch custom functions + Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction + Utils::Hook(0x4EC88E, Script::Player_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick(); @@ -747,7 +771,7 @@ namespace Components Script::LastFrameTime = nowMs; }); - Script::AddFunction("DebugBox", [](Game::scr_entref_t) + Script::AddFunction("DebugBox", []() { const auto* message = Game::Scr_GetString(0); @@ -772,7 +796,10 @@ namespace Components Script::ScriptName.clear(); Script::ScriptHandles.clear(); Script::ScriptNameStack.clear(); - Script::ScriptFunctions.clear(); + + Script::CustomScrFunctions.clear(); + Script::CustomScrMethods.clear(); + Script::ReplacedFunctions.clear(); Script::VMShutdownSignal.clear(); diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/Script.hpp index c1de5331..1d1144aa 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/Script.hpp @@ -6,26 +6,13 @@ namespace Components class Script : public Component { public: - class Function - { - public: - Function(const std::string& _name, Game::scr_function_t _callback, bool _dev) : name(_name), callback(_callback), dev(_dev) {} - - const char* getName() const { return this->name.data(); } - bool isDev() const { return this->dev; } - Game::scr_function_t getFunction() const { return this->callback; } - - private: - std::string name; - Game::scr_function_t callback; - bool dev; - }; - Script(); ~Script(); static int LoadScriptAndLabel(const std::string& script, const std::string& label); - static void AddFunction(const std::string& name, Game::scr_function_t function, bool isDev = false); + + static void AddFunction(const char* name, Game::xfunction_t func, int type = 0); + static void AddMethod(const char* name, Game::xmethod_t func, int type = 0); static void OnVMShutdown(Utils::Slot callback); @@ -35,7 +22,8 @@ namespace Components private: static std::string ScriptName; static std::vector ScriptHandles; - static std::vector ScriptFunctions; + static std::unordered_map CustomScrFunctions; + static std::unordered_map CustomScrMethods; static std::vector ScriptNameStack; static unsigned short FunctionName; static std::unordered_map ScriptStorage; @@ -62,8 +50,11 @@ namespace Components static void LoadGameType(); static void LoadGameTypeScript(); - static Game::scr_function_t GetFunction(void* caller, const char** name, int* isDev); - static void GetFunctionStub(); + static Game::xfunction_t Scr_GetFunctionStub(const char** pName, int* type); + static Game::xmethod_t Scr_GetMethodStub(const char** pName, int* type); + + static Game::xfunction_t BuiltIn_GetFunctionStub(const char** pName, int* type); + static Game::xmethod_t Player_GetMethodStub(const char** pName); static void ScrShutdownSystemStub(int); static void StoreScriptBaseProgramNumStub(); diff --git a/src/Components/Modules/ScriptExtension.cpp b/src/Components/Modules/ScriptExtension.cpp index 1a852ae9..a6823ea8 100644 --- a/src/Components/Modules/ScriptExtension.cpp +++ b/src/Components/Modules/ScriptExtension.cpp @@ -8,7 +8,7 @@ namespace Components { //File functions - Script::AddFunction("FileWrite", [](Game::scr_entref_t) // gsc: FileWrite(, , ) + Script::AddFunction("FileWrite", []() // gsc: FileWrite(, , ) { const auto* path = Game::Scr_GetString(0); auto* text = Game::Scr_GetString(1); @@ -51,7 +51,7 @@ namespace Components } }); - Script::AddFunction("FileRead", [](Game::scr_entref_t) // gsc: FileRead() + Script::AddFunction("FileRead", []() // gsc: FileRead() { const auto* path = Game::Scr_GetString(0); @@ -79,7 +79,7 @@ namespace Components Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data()); }); - Script::AddFunction("FileExists", [](Game::scr_entref_t) // gsc: FileExists() + Script::AddFunction("FileExists", []() // gsc: FileExists() { const auto* path = Game::Scr_GetString(0); @@ -101,7 +101,7 @@ namespace Components Game::Scr_AddInt(FileSystem::FileReader(path).exists()); }); - Script::AddFunction("FileRemove", [](Game::scr_entref_t) // gsc: FileRemove() + Script::AddFunction("FileRemove", []() // gsc: FileRemove() { const auto* path = Game::Scr_GetString(0); @@ -130,7 +130,7 @@ namespace Components void ScriptExtension::AddMethods() { // ScriptExtension methods - Script::AddFunction("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp() + Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp() { const auto* gentity = Script::GetEntity(entref); const auto* client = Script::GetClient(gentity); @@ -145,7 +145,7 @@ namespace Components Game::Scr_AddString(ip.data()); }); - Script::AddFunction("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing() + Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing() { const auto* gentity = Script::GetEntity(entref); const auto* client = Script::GetClient(gentity); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index eea681a5..31926ec4 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -699,7 +699,7 @@ namespace Game typedef void(__cdecl * Scr_ClearOutParams_t)(); extern Scr_ClearOutParams_t Scr_ClearOutParams; - typedef void(__cdecl * Scr_RegisterFunction_t)(scr_function_t function); + typedef void(__cdecl * Scr_RegisterFunction_t)(int func, const char* name); extern Scr_RegisterFunction_t Scr_RegisterFunction; typedef bool(__cdecl * Scr_IsSystemActive_t)(); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 84792969..621523d2 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -26,7 +26,22 @@ namespace Game unsigned __int16 classnum; }; - typedef void(__cdecl * scr_function_t)(scr_entref_t); + typedef void(__cdecl * xfunction_t)(); + typedef void(__cdecl * xmethod_t)(scr_entref_t); + + struct BuiltinFunctionDef + { + const char* actionString; + xfunction_t actionFunc; + int type; + }; + + struct BuiltinMethodDef + { + const char* actionString; + xmethod_t actionFunc; + int type; + }; enum XAssetType {