mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
feat(lua): implement latent functions
This commit is contained in:
@@ -68,7 +68,6 @@ namespace YimMenu
|
||||
|
||||
void ScriptMgr::YieldImpl(std::optional<std::chrono::high_resolution_clock::duration> time)
|
||||
{
|
||||
// TODO: this wouldn't work!!! fix ASAP!
|
||||
if (auto script = LuaManager::GetRunningCoroutine())
|
||||
LuaScript::GetScript(script).Yield(script, time ? std::chrono::duration_cast<std::chrono::milliseconds>(*time).count() : 0);
|
||||
else if (auto script = static_cast<Script*>(GetFiberData()))
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "LatentFunction.hpp"
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
static void LatentFiber(LPVOID parameter)
|
||||
{
|
||||
auto callback = reinterpret_cast<LuaScript::ScriptCallback*>(parameter);
|
||||
callback->m_LastReturnValue = callback->m_LatentTarget(callback->m_CoroState);
|
||||
SwitchToFiber(callback->m_ParentFiber);
|
||||
}
|
||||
|
||||
int LatentFunctionInternal(lua_State* state, lua_CFunction func)
|
||||
{
|
||||
auto& script = LuaScript::GetScript(state);
|
||||
auto callback = script.GetRunningCallback();
|
||||
|
||||
if (!callback)
|
||||
luaL_error(state, "Attempted to call a latent function outside a script callback");
|
||||
|
||||
// destroy remnants from a previous latent function call
|
||||
if (callback->m_Fiber)
|
||||
{
|
||||
DeleteFiber(callback->m_Fiber);
|
||||
callback->m_Fiber = nullptr;
|
||||
}
|
||||
|
||||
callback->m_Fiber = CreateFiber(0, &LatentFiber, callback); // TODO: maybe create a latent function fiber pool?
|
||||
callback->m_ParentFiber = GetCurrentFiber(); // the scripting system has already turned the main thread into a fiber
|
||||
callback->m_LatentTarget = func;
|
||||
callback->m_CoroState = state;
|
||||
callback->m_LastReturnValue = -1;
|
||||
|
||||
SwitchToFiber(callback->m_Fiber); // switch to the new fiber
|
||||
// we'll be back eventually
|
||||
|
||||
return script.GetRunningCallback()->m_LastReturnValue;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
int LatentFunction(lua_State* lua)
|
||||
int LatentFunctionInternal(lua_State* state, lua_CFunction func);
|
||||
|
||||
template<lua_CFunction function>
|
||||
int LatentFunction(lua_State* state)
|
||||
{
|
||||
|
||||
return LatentFunctionInternal(state, function);
|
||||
}
|
||||
}
|
||||
@@ -168,6 +168,8 @@ namespace YimMenu
|
||||
callback.m_Fiber = nullptr;
|
||||
callback.m_ParentFiber = nullptr;
|
||||
callback.m_LatentTarget = nullptr;
|
||||
callback.m_CoroState = nullptr;
|
||||
callback.m_LastReturnValue = -1;
|
||||
|
||||
// we don't want to push any additional callbacks to the main array when we're in the middle of running, and potentially deleting, them
|
||||
if (m_RunningScriptCallbacks)
|
||||
@@ -180,7 +182,18 @@ namespace YimMenu
|
||||
{
|
||||
lua_pushinteger(state, millis);
|
||||
lua_pushboolean(state, from_code);
|
||||
lua_yield(state, 2);
|
||||
if (from_code)
|
||||
{
|
||||
if (lua_isyieldable(state) && lua_status(state) != LUA_YIELD) // only yield for the first time, since after that we're not yielding for real
|
||||
lua_yield(state, 2);
|
||||
GetScript(state).m_CurrentlyExecutingCallback->m_LastReturnValue = -1;
|
||||
SwitchToFiber(GetScript(state).m_CurrentlyExecutingCallback->m_ParentFiber);
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_yield(state, 2);
|
||||
// function must return -1 immediately after this
|
||||
}
|
||||
}
|
||||
|
||||
void LuaScript::Tick()
|
||||
@@ -194,7 +207,41 @@ namespace YimMenu
|
||||
lua_State* coro_state = lua_tothread(m_State, -1);
|
||||
lua_pop(m_State, 1);
|
||||
|
||||
auto state = ResumeCoroutine(0, 2, coro_state);
|
||||
int num_args = 0;
|
||||
|
||||
if (callback.m_LastYieldFromCode)
|
||||
{
|
||||
// we need to pretend we're running in a coroutine
|
||||
LuaManager::SetRunningCoroutine(coro_state);
|
||||
m_CurrentlyExecutingCallback = &callback;
|
||||
SwitchToFiber(callback.m_Fiber);
|
||||
m_CurrentlyExecutingCallback = nullptr;
|
||||
LuaManager::SetRunningCoroutine(nullptr);
|
||||
|
||||
if (callback.m_LastReturnValue < 0)
|
||||
{
|
||||
// yielded again into a latent function
|
||||
auto time = lua_tointeger(m_State, -2);
|
||||
lua_pop(m_State, 2);
|
||||
|
||||
callback.SetTimeToResume(time);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// done with this one
|
||||
callback.m_LastYieldFromCode = false;
|
||||
num_args = callback.m_LastReturnValue;
|
||||
}
|
||||
}
|
||||
|
||||
LuaManager::SetRunningCoroutine(coro_state);
|
||||
m_CurrentlyExecutingCallback = &callback;
|
||||
|
||||
auto state = ResumeCoroutine(num_args, 2, coro_state);
|
||||
|
||||
m_CurrentlyExecutingCallback = nullptr;
|
||||
LuaManager::SetRunningCoroutine(nullptr);
|
||||
|
||||
if (state != LUA_YIELD)
|
||||
{
|
||||
@@ -206,14 +253,7 @@ namespace YimMenu
|
||||
auto from_code = lua_toboolean(m_State, -1);
|
||||
lua_pop(m_State, 2);
|
||||
|
||||
if (time == 0)
|
||||
{
|
||||
callback.m_TimeToResume = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.m_TimeToResume = std::chrono::high_resolution_clock::now() + std::chrono::duration_cast<std::chrono::high_resolution_clock::duration>(std::chrono::milliseconds(static_cast<std::uint64_t>(time)));
|
||||
}
|
||||
callback.SetTimeToResume(time);
|
||||
callback.m_LastYieldFromCode = from_code;
|
||||
|
||||
return false;
|
||||
@@ -223,4 +263,16 @@ namespace YimMenu
|
||||
std::ranges::move(m_QueuedScriptCallbacks, std::back_inserter(m_ScriptCallbacks));
|
||||
m_QueuedScriptCallbacks.clear();
|
||||
}
|
||||
|
||||
void LuaScript::ScriptCallback::SetTimeToResume(int millis)
|
||||
{
|
||||
if (millis == 0)
|
||||
{
|
||||
m_TimeToResume = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_TimeToResume = std::chrono::high_resolution_clock::now() + std::chrono::duration_cast<std::chrono::high_resolution_clock::duration>(std::chrono::milliseconds(static_cast<std::uint64_t>(millis)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ namespace YimMenu
|
||||
UNLOADED
|
||||
};
|
||||
|
||||
private:
|
||||
struct ScriptCallback
|
||||
{
|
||||
int m_Coroutine;
|
||||
@@ -25,9 +24,13 @@ namespace YimMenu
|
||||
PVOID m_Fiber;
|
||||
PVOID m_ParentFiber;
|
||||
lua_CFunction m_LatentTarget;
|
||||
lua_State* m_CoroState;
|
||||
int m_LastReturnValue;
|
||||
|
||||
void SetTimeToResume(int millis);
|
||||
};
|
||||
|
||||
private:
|
||||
LoadState m_LoadState = LoadState::EMPTY;
|
||||
lua_State* m_State = nullptr;
|
||||
std::string m_FileName;
|
||||
@@ -36,6 +39,7 @@ namespace YimMenu
|
||||
std::vector<ScriptCallback> m_ScriptCallbacks;
|
||||
std::vector<ScriptCallback> m_QueuedScriptCallbacks;
|
||||
bool m_RunningScriptCallbacks = false;
|
||||
ScriptCallback* m_CurrentlyExecutingCallback = nullptr;
|
||||
|
||||
// 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);
|
||||
@@ -118,5 +122,10 @@ namespace YimMenu
|
||||
void Yield(lua_State* state, int millis = 0, bool from_code = true);
|
||||
|
||||
void Tick();
|
||||
|
||||
ScriptCallback* GetRunningCallback()
|
||||
{
|
||||
return m_CurrentlyExecutingCallback;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "core/scripting/LuaLibrary.hpp"
|
||||
#include "core/scripting/LuaScript.hpp"
|
||||
#include "core/scripting/LatentFunction.hpp"
|
||||
#include "game/gta/Vehicle.hpp"
|
||||
#include "game/backend/Self.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
// only use for internal testing
|
||||
class Internal : LuaLibrary
|
||||
{
|
||||
using LuaLibrary::LuaLibrary;
|
||||
|
||||
static int SpawnVehicle(lua_State* state)
|
||||
{
|
||||
const char* model = luaL_checkstring(state, 1);
|
||||
Vehicle::Create(Joaat(model), Self::GetPed().GetPosition());
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void Register(lua_State* state) override
|
||||
{
|
||||
lua_newtable(state);
|
||||
|
||||
lua_pushcfunction(state, LatentFunction<SpawnVehicle>);
|
||||
lua_setfield(state, -2, "spawn_vehicle");
|
||||
|
||||
lua_setglobal(state, "internal");
|
||||
}
|
||||
};
|
||||
|
||||
Internal _Internal;
|
||||
}
|
||||
Reference in New Issue
Block a user