diff --git a/src/core/scripting/LuaManager.cpp b/src/core/scripting/LuaManager.cpp index 90abdf2..d1f39a3 100644 --- a/src/core/scripting/LuaManager.cpp +++ b/src/core/scripting/LuaManager.cpp @@ -15,6 +15,22 @@ namespace YimMenu m_Libraries.push_back(library); } + int LuaManager::RegisterResourceTypeImpl(LuaResourceType* res_type) + { + m_ResourceTypes.push_back(res_type); + return m_ResourceTypes.size() - 1; + } + + int LuaManager::GetNumResourceTypesImpl() + { + return m_ResourceTypes.size(); + } + + LuaResourceType* LuaManager::GetResourceTypeImpl(int index) + { + return m_ResourceTypes[index]; + } + void LuaManager::LoadLibrariesImpl(lua_State* state) { for (auto library : m_Libraries) @@ -115,7 +131,7 @@ namespace YimMenu // 4) run tick coroutines for (auto& script : m_LoadedScripts) - if (script->IsValid()) + if (script->IsRunning()) script->Tick(); ScriptMgr::Yield(); @@ -160,7 +176,7 @@ namespace YimMenu for (auto& script : m_LoadedScripts) { - if (script->IsValid()) + if (script->IsRunning()) { result = (bool)(((int)result) & ((int)script->DispatchEvent(event, add_arguments_cb, handle_result))); if (!result && handle_result) @@ -170,4 +186,32 @@ namespace YimMenu return result; } + + void LuaManager::ForAllResourcesOfTypeImpl(ForAllResourcesOfTypeCallback callback, int type) + { + auto _type = GetResourceType(type); + bool locked = false; + for (auto& script : m_LoadedScripts) + { + if (script->IsRunning()) + { + auto& resources = script->GetAllResourcesOfType(type); + if (resources.size() > 0) + { + if (!locked) + { + _type->Lock(); + locked = true; + } + + for (auto& resource : resources) + { + callback(resource.get()); + } + } + } + } + if (locked) + _type->Unlock(); + } } \ No newline at end of file diff --git a/src/core/scripting/LuaManager.hpp b/src/core/scripting/LuaManager.hpp index 1ef8429..b278649 100644 --- a/src/core/scripting/LuaManager.hpp +++ b/src/core/scripting/LuaManager.hpp @@ -1,6 +1,7 @@ #pragma once #include "LuaScript.hpp" #include "LuaLibrary.hpp" +#include "LuaResource.hpp" namespace YimMenu { @@ -16,12 +17,14 @@ namespace YimMenu using ForAllLoadedScriptsCallback = void(*)(std::shared_ptr& script); using ForAllUnloadedScriptsCallback = void(*)(UnloadedScript& script); + using ForAllResourcesOfTypeCallback = void(*)(LuaResource* resource); private: std::vector> m_LoadedScripts; std::vector m_UnloadedScripts; std::chrono::system_clock::time_point m_LastRefreshedUnloadedScripts; std::vector m_Libraries; + std::vector m_ResourceTypes; std::queue m_ScriptsToLoad; std::mutex m_LoadMutex; std::uint32_t m_MainThreadId; @@ -31,6 +34,9 @@ namespace YimMenu void AddUnloadedScript(std::string_view name, std::string_view path); void RegisterLibraryImpl(LuaLibrary* library); + int RegisterResourceTypeImpl(LuaResourceType* res_type); // returns resource index + int GetNumResourceTypesImpl(); + LuaResourceType* GetResourceTypeImpl(int index); void LoadLibrariesImpl(lua_State* state); void LoadScriptImpl(std::string path); void RunScriptImpl(); @@ -39,6 +45,7 @@ namespace YimMenu void ForAllLoadedScriptsImpl(ForAllLoadedScriptsCallback callback); void ForAllUnloadedScriptsImpl(ForAllUnloadedScriptsCallback callback); bool DispatchEventImpl(std::uint32_t event, const LuaScript::DispatchEventCallback& add_arguments_cb, bool handle_result = false); + void ForAllResourcesOfTypeImpl(ForAllResourcesOfTypeCallback callback, int type); static LuaManager& GetInstance() { @@ -52,6 +59,21 @@ namespace YimMenu GetInstance().RegisterLibraryImpl(library); } + static int RegisterResourceType(LuaResourceType* res_type) + { + return GetInstance().RegisterResourceTypeImpl(res_type); + } + + static int GetNumResourceTypes() + { + return GetInstance().GetNumResourceTypesImpl(); + } + + static LuaResourceType* GetResourceType(int index) + { + return GetInstance().GetResourceTypeImpl(index); + } + static void LoadLibraries(lua_State* state) { GetInstance().LoadLibrariesImpl(state); diff --git a/src/core/scripting/LuaResource.cpp b/src/core/scripting/LuaResource.cpp new file mode 100644 index 0000000..279dff1 --- /dev/null +++ b/src/core/scripting/LuaResource.cpp @@ -0,0 +1,10 @@ +#include "LuaResource.hpp" +#include "LuaManager.hpp" + +namespace YimMenu +{ + void LuaResourceType::Register() + { + m_Index = LuaManager::RegisterResourceType(this); + } +} \ No newline at end of file diff --git a/src/core/scripting/LuaResource.hpp b/src/core/scripting/LuaResource.hpp new file mode 100644 index 0000000..2a37b08 --- /dev/null +++ b/src/core/scripting/LuaResource.hpp @@ -0,0 +1,43 @@ +#pragma once + +namespace YimMenu +{ + class LuaResource + { + int m_Type; + + public: + int GetType() const { return m_Type; } + void SetType(int type) { m_Type = type; } + virtual void OnEnable() {}; // called during resume and also immediately after AddResource + virtual void OnDisable() {}; // called during pause and before unload + }; + + class LuaResourceType + { + int m_Index{}; + std::mutex m_Mutex{}; + + void Register(); + public: + LuaResourceType() + { + Register(); + } + + void Lock() + { + m_Mutex.lock(); + } + + void Unlock() + { + m_Mutex.unlock(); + } + + int GetIndex() const + { + return m_Index; + } + }; +} \ No newline at end of file diff --git a/src/core/scripting/LuaScript.cpp b/src/core/scripting/LuaScript.cpp index a479b1a..75e6603 100644 --- a/src/core/scripting/LuaScript.cpp +++ b/src/core/scripting/LuaScript.cpp @@ -109,10 +109,45 @@ namespace YimMenu DeleteFiber(callback.m_Fiber); } + void LuaScript::DisableResources() + { + for (int i = 0; i < m_Resources.size(); i++) + { + if (!m_Resources[i].size()) + return; + + auto type = LuaManager::GetResourceType(i); + type->Lock(); + + for (auto& resource : m_Resources[i]) + resource->OnDisable(); + + type->Unlock(); + } + } + + void LuaScript::EnableResources() + { + for (int i = 0; i < m_Resources.size(); i++) + { + if (!m_Resources[i].size()) + return; + + auto type = LuaManager::GetResourceType(i); + type->Lock(); + + for (auto& resource : m_Resources[i]) + resource->OnEnable(); + + type->Unlock(); + } + } + LuaScript::LuaScript(std::string_view file_name) : m_FileName(file_name), m_ModuleName(std::filesystem::path(file_name).filename().string()) { + m_Resources.resize(LuaManager::GetNumResourceTypes()); m_State = luaL_newstate(); lua_pushlightuserdata(m_State, (void*)this); @@ -143,6 +178,30 @@ namespace YimMenu } } + void LuaScript::Pause() + { + if (m_LoadState == LoadState::RUNNING) + { + DisableResources(); + m_LoadState = LoadState::PAUSED; + } + } + + void LuaScript::Resume() + { + if (m_LoadState == LoadState::PAUSED) + { + EnableResources(); + m_LoadState = LoadState::RUNNING; + } + } + + void LuaScript::MarkUnloaded() + { + DisableResources(); + m_LoadState = LoadState::UNLOADED; + } + bool LuaScript::SafeToUnload() { for (auto& callback : m_ScriptCallbacks) @@ -304,6 +363,25 @@ namespace YimMenu return result; } + + void LuaScript::AddResource(std::shared_ptr&& resource, int idx) + { + resource->SetType(idx); // TODO: this is a bad idea + auto type = LuaManager::GetResourceType(idx); + type->Lock(); + m_Resources[resource->GetType()].push_back(std::move(resource)); + type->Unlock(); + } + + int LuaScript::GetNumResourcesOfType(int type) + { + return m_Resources[type].size(); + } + + std::vector>& LuaScript::GetAllResourcesOfType(int idx) + { + return m_Resources[idx]; + } void LuaScript::ScriptCallback::SetTimeToResume(int millis) { diff --git a/src/core/scripting/LuaScript.hpp b/src/core/scripting/LuaScript.hpp index fdd7bb7..ea5eec8 100644 --- a/src/core/scripting/LuaScript.hpp +++ b/src/core/scripting/LuaScript.hpp @@ -1,5 +1,6 @@ #pragma once #include "lua.hpp" +#include "LuaResource.hpp" namespace YimMenu { @@ -43,12 +44,14 @@ namespace YimMenu bool m_RunningScriptCallbacks = false; ScriptCallback* m_CurrentlyExecutingCallback = nullptr; std::unordered_map> m_EventHandlers; - bool m_NativesLoaded = false; // TODO: move this to a LuaResource + std::vector>> m_Resources; // yes, it's a shared pointer stored in a vector of resources stored in a vector of resource types TODO: can we just use raw pointers or even store the resource directly in that array? // Calls the function at the top of stack. If this returns false the stack would have nothing on it bool CallFunction(int n_args, int n_results, lua_State* override_state = nullptr); int ResumeCoroutine(int n_args, int n_results, lua_State* coro_state); void RemoveScriptCallback(ScriptCallback& callback); + void DisableResources(); + void EnableResources(); public: LuaScript(std::string_view file_name); @@ -59,7 +62,7 @@ namespace YimMenu return m_IsMalfunctioning; } - bool IsValid() const + bool IsRunning() const { return m_LoadState == LoadState::RUNNING; } @@ -86,23 +89,11 @@ namespace YimMenu m_LoadState = LoadState::WANT_RELOAD; } - void Pause() - { - if (m_LoadState == LoadState::RUNNING) - m_LoadState = LoadState::PAUSED; - } - - void Resume() - { - if (m_LoadState == LoadState::PAUSED) - m_LoadState = LoadState::RUNNING; - } + void Pause(); + void Resume(); // Should only be called by LuaManager::RunScriptImpl - void MarkUnloaded() - { - m_LoadState = LoadState::UNLOADED; - } + void MarkUnloaded(); bool SafeToUnload(); @@ -135,18 +126,9 @@ namespace YimMenu void AddEventHandler(std::uint32_t event, int handler); bool DispatchEvent(std::uint32_t event, const DispatchEventCallback& add_arguments_cb, bool handle_result = false); - // ------- TODO: move these into a resource - - bool AreNativesLoaded() const - { - return m_NativesLoaded; - } - - void SetNativesLoaded() - { - m_NativesLoaded = true; - } - - // -------------- + // TODO: add RemoveResource + void AddResource(std::shared_ptr&& resource, int idx); + int GetNumResourcesOfType(int type); + std::vector>& GetAllResourcesOfType(int idx); }; } \ No newline at end of file diff --git a/src/game/gta/Entity.cpp b/src/game/gta/Entity.cpp index 0e7bae8..ed103f2 100644 --- a/src/game/gta/Entity.cpp +++ b/src/game/gta/Entity.cpp @@ -45,7 +45,7 @@ namespace YimMenu { if (!IsValid()) { - LOG(WARNING) << "IsValid() assertion failed for " << function_name; + LOG(WARNING) << "IsRunning() assertion failed for " << function_name; } } diff --git a/src/game/scripting/libraries/Natives.cpp b/src/game/scripting/libraries/Natives.cpp index e57840c..334e6bc 100644 --- a/src/game/scripting/libraries/Natives.cpp +++ b/src/game/scripting/libraries/Natives.cpp @@ -1,4 +1,5 @@ #include "core/scripting/LuaLibrary.hpp" +#include "core/scripting/LuaResource.hpp" #include "core/scripting/LuaScript.hpp" #include "core/scripting/LuaUtils.hpp" #include "NativeDefs.hpp" @@ -6,6 +7,12 @@ namespace YimMenu::Lua { + class AreNativesLoadedResource : public LuaResource + { + }; + + const static LuaResourceType _AreNativesLoadedResource; + class Natives : LuaLibrary { using LuaLibrary::LuaLibrary; @@ -14,7 +21,7 @@ namespace YimMenu::Lua { auto& script = LuaScript::GetScript(state); - if (script.AreNativesLoaded()) + if (script.GetNumResourcesOfType(_AreNativesLoadedResource.GetIndex()) > 0) luaL_error(state, "Natives have already been loaded"); for (int i = 0; i < g_NumLuaNativeDefs; i++) @@ -28,14 +35,14 @@ namespace YimMenu::Lua } } - script.SetNativesLoaded(); + script.AddResource(std::make_shared(), _AreNativesLoadedResource.GetIndex()); return 0; } static int AreNativesLoaded(lua_State* state) { auto& script = LuaScript::GetScript(state); - lua_pushboolean(state, script.AreNativesLoaded()); + lua_pushboolean(state, script.GetNumResourcesOfType(_AreNativesLoadedResource.GetIndex()) > 0); return 1; } diff --git a/src/game/scripting/libraries/Ped.cpp b/src/game/scripting/libraries/Ped.cpp new file mode 100644 index 0000000..261459e --- /dev/null +++ b/src/game/scripting/libraries/Ped.cpp @@ -0,0 +1,264 @@ +#include "core/scripting/LuaLibrary.hpp" +#include "core/scripting/LuaScript.hpp" +#include "core/scripting/LuaUtils.hpp" +#include "core/scripting/LatentFunction.hpp" +#include "game/gta/Ped.hpp" +#include "Entity.hpp" + +namespace YimMenu::Lua +{ + class Ped : LuaLibrary + { + public: + using LuaLibrary::LuaLibrary; + + static int New(lua_State* state) + { + CreateObject(state, luaL_checkinteger(state, 1)); + return 1; + } + + static int Create(lua_State* state) + { + CopyObject(state, YimMenu::Ped::Create(GetHashArgument(state, 1), GetObject(state, 2), lua_gettop(state) >= 3 ? luaL_checknumber(state, 3) : 0.0f)); + return 1; + } + + static int GetVehicle(lua_State* state) + { + CopyObject(state, GetObject(state, 1).GetVehicle()); + return 1; + } + + static int GetLastVehicle(lua_State* state) + { + CopyObject(state, GetObject(state, 1).GetLastVehicle()); + return 1; + } + + static int GetVehicleObjectId(lua_State* state) + { + lua_pushinteger(state, GetObject(state, 1).GetVehicleObjectId()); + return 1; + } + + static int SetInVehicle(lua_State* state) + { + GetObject(state, 1).SetInVehicle(GetObject(state, 2), lua_gettop(state) >= 3 ? luaL_checkinteger(state, 3) : 0); + return 0; + } + + static int GetRagdoll(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).GetRagdoll()); + return 1; + } + + static int SetRagdoll(lua_State* state) + { + GetObject(state, 1).SetRagdoll(CheckBooleanSafe(state, 2)); + return 0; + } + + static int GetBonePosition(lua_State* state) + { + MoveObject(state, GetObject(state, 1).GetBonePosition(luaL_checkinteger(state, 2))); + return 0; + } + + // GetConfigFlag + // SetConfigFlag + // SetCombatAttribute + + static int IsEnemy(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).IsEnemy()); + return 1; + } + + static int GetAccuracy(lua_State* state) + { + lua_pushinteger(state, GetObject(state, 1).GetAccuracy()); + return 1; + } + + static int SetAccuracy(lua_State* state) + { + GetObject(state, 1).SetAccuracy(luaL_checkinteger(state, 2)); + return 0; + } + + // GetPlayer + + static int GiveWeapon(lua_State* state) + { + GetObject(state, 1).GiveWeapon(GetHashArgument(state, 2), lua_gettop(state) >= 3 ? CheckBooleanSafe(state, 3) : false); + return 0; + } + + static int RemoveWeapon(lua_State* state) + { + GetObject(state, 1).RemoveWeapon(GetHashArgument(state, 2)); + return 0; + } + + static int GetCurrentWeapon(lua_State* state) + { + lua_pushinteger(state, GetObject(state, 1).GetCurrentWeapon()); + return 1; + } + + static int HasWeapon(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).HasWeapon(GetHashArgument(state, 2))); + return 1; + } + + static int SetInfiniteAmmo(lua_State* state) + { + GetObject(state, 1).SetInfiniteAmmo(CheckBooleanSafe(state, 2)); + return 0; + } + + static int SetInfiniteClip(lua_State* state) + { + GetObject(state, 1).SetInfiniteClip(CheckBooleanSafe(state, 2)); + return 0; + } + + static int SetMaxAmmoForWeapon(lua_State* state) + { + GetObject(state, 1).SetMaxAmmoForWeapon(GetHashArgument(state, 2)); + return 0; + } + + static int TeleportTo(lua_State* state) + { + GetObject(state, 1).TeleportTo(GetObject(state, 2)); + return 0; + } + + static int GetArmour(lua_State* state) + { + lua_pushinteger(state, GetObject(state, 1).GetArmour()); + return 1; + } + + static int SetArmour(lua_State* state) + { + GetObject(state, 1).SetArmour(luaL_checkinteger(state, 2)); + return 0; + } + + static int SetLeaderOfGroup(lua_State* state) + { + GetObject(state, 1).SetLeaderOfGroup(luaL_checkinteger(state, 2)); + return 0; + } + + static int AddToGroup(lua_State* state) + { + GetObject(state, 1).AddToGroup(luaL_checkinteger(state, 2)); + return 0; + } + + static int RemoveFromGroup(lua_State* state) + { + GetObject(state, 1).RemoveFromGroup(); + return 0; + } + + static int IsMemberOfGroup(lua_State* state) + { + lua_pushboolean(state, GetObject(state, 1).IsMemberOfGroup(luaL_checkinteger(state, 2))); + return 1; + } + + static int RandomizeOutfit(lua_State* state) + { + GetObject(state, 1).RandomizeOutfit(); + return 0; + } + + static int StartScenario(lua_State* state) + { + GetObject(state, 1).StartScenario(CheckStringSafe(state, 2), lua_gettop(state) >= 3 ? luaL_checkinteger(state, 3) : -1, lua_gettop(state) >= 4 ? CheckBooleanSafe(state, 4) : true); + return 0; + } + + static int SetKeepTask(lua_State* state) + { + GetObject(state, 1).SetKeepTask(CheckBooleanSafe(state, 2)); + return 0; + } + + static int ClearDamage(lua_State* state) + { + GetObject(state, 1).ClearDamage(); + return 0; + } + + static int SetMaxTimeUnderwater(lua_State* state) + { + GetObject(state, 1).SetMaxTimeUnderwater(luaL_checkinteger(state, 2)); + return 0; + } + + static int SetAsCop(lua_State* state) + { + GetObject(state, 1).SetAsCop(); + return 0; + } + + virtual void Register(lua_State* state) override + { + luaL_newmetatable(state, "Ped"); + { + lua_newtable(state); + { + RegisterEntityMethods(state); // re-registering the entity methods under the vehicle table is more efficient than subclassing + SetFunction(state, GetVehicle, "get_vehicle"); + SetFunction(state, GetLastVehicle, "get_last_vehicle"); + SetFunction(state, GetVehicleObjectId, "get_vehicle_object_id"); + SetFunction(state, SetInVehicle, "set_in_vehicle"); + SetFunction(state, GetRagdoll, "get_ragdoll"); + SetFunction(state, SetRagdoll, "set_ragdoll"); + SetFunction(state, GetBonePosition, "get_bone_position"); + SetFunction(state, IsEnemy, "is_enemy"); + SetFunction(state, GetAccuracy, "get_accuracy"); + SetFunction(state, SetAccuracy, "set_accuracy"); + SetFunction(state, GiveWeapon, "give_weapon"); + SetFunction(state, RemoveWeapon, "remove_weapon"); + SetFunction(state, GetCurrentWeapon, "get_current_weapon"); + SetFunction(state, HasWeapon, "has_weapon"); + SetFunction(state, SetInfiniteAmmo, "set_infinite_ammo"); + SetFunction(state, SetInfiniteClip, "set_infinite_clip"); + SetFunction(state, SetMaxAmmoForWeapon, "set_max_ammo_for_weapon"); + SetFunction(state, TeleportTo, "teleport_to"); + SetFunction(state, GetArmour, "get_armour"); + SetFunction(state, SetArmour, "set_armour"); + SetFunction(state, SetLeaderOfGroup, "set_leader_of_group"); + SetFunction(state, AddToGroup, "add_to_group"); + SetFunction(state, RemoveFromGroup, "remove_from_group"); + SetFunction(state, IsMemberOfGroup, "is_member_of_group"); + SetFunction(state, RandomizeOutfit, "randomize_outfit"); + SetFunction(state, StartScenario, "start_scenario"); + SetFunction(state, SetKeepTask, "set_keep_task"); + SetFunction(state, ClearDamage, "clear_damage"); + SetFunction(state, SetMaxTimeUnderwater, "set_max_time_underwater"); + SetFunction(state, SetAsCop, "set_as_cop"); + } + lua_setfield(state, -2, "__index"); // prototype + } + Metatable::Register(state); + Metatable::AddSubclass(); + + lua_newtable(state); + SetConstructor(state); + SetFunction(state, LatentFunction, "create"); + lua_setglobal(state, "Ped"); + } + }; + + Ped _Ped; +}