mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
feat(lua): implement events
This commit is contained in:
@@ -5,6 +5,7 @@ namespace YimMenu::Lua
|
||||
{
|
||||
int LatentFunctionInternal(lua_State* state, lua_CFunction func);
|
||||
|
||||
// a latent function is a function that can yield
|
||||
template<lua_CFunction function>
|
||||
int LatentFunction(lua_State* state)
|
||||
{
|
||||
|
||||
@@ -95,10 +95,16 @@ namespace YimMenu
|
||||
continue;
|
||||
|
||||
for (auto& script : m_LoadedScripts)
|
||||
{
|
||||
if (std::filesystem::equivalent(script->GetPath(), entry.path().string()))
|
||||
continue;
|
||||
{
|
||||
// continue;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
AddUnloadedScript(entry.path().filename().string(), entry.path().string());
|
||||
next:
|
||||
}
|
||||
m_LastRefreshedUnloadedScripts = std::chrono::system_clock::now();
|
||||
}
|
||||
@@ -144,4 +150,21 @@ namespace YimMenu
|
||||
for (auto& script : m_UnloadedScripts)
|
||||
callback(script);
|
||||
}
|
||||
|
||||
bool LuaManager::DispatchEventImpl(std::uint32_t event, const LuaScript::DispatchEventCallback& add_arguments_cb, bool handle_result)
|
||||
{
|
||||
auto result = true;
|
||||
|
||||
for (auto& script : m_LoadedScripts)
|
||||
{
|
||||
if (script->IsValid())
|
||||
{
|
||||
result = (bool)(((int)result) & ((int)script->DispatchEvent(event, add_arguments_cb, handle_result)));
|
||||
if (!result && handle_result)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ namespace YimMenu
|
||||
void SetRunningCoroutineImpl(lua_State* script);
|
||||
void ForAllLoadedScriptsImpl(ForAllLoadedScriptsCallback callback);
|
||||
void ForAllUnloadedScriptsImpl(ForAllUnloadedScriptsCallback callback);
|
||||
bool DispatchEventImpl(std::uint32_t event, const LuaScript::DispatchEventCallback& add_arguments_cb, bool handle_result = false);
|
||||
|
||||
static LuaManager& GetInstance()
|
||||
{
|
||||
@@ -95,5 +96,12 @@ namespace YimMenu
|
||||
{
|
||||
GetInstance().ForAllUnloadedScriptsImpl(callback);
|
||||
}
|
||||
|
||||
// if handle_result is true, the event will be blocked when a callback returns false
|
||||
// events MUST be dispatched from the main thread
|
||||
static bool DispatchEvent(std::uint32_t event, const LuaScript::DispatchEventCallback& add_arguments_cb, bool handle_result = false)
|
||||
{
|
||||
return GetInstance().DispatchEventImpl(event, add_arguments_cb, handle_result);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -119,8 +119,9 @@ namespace YimMenu
|
||||
lua_setfield(m_State, LUA_REGISTRYINDEX, "context");
|
||||
|
||||
LuaManager::LoadLibraries(m_State);
|
||||
|
||||
if (luaL_loadfilex(m_State, file_name.data(), "t") != LUA_OK) // don't load binary chunks
|
||||
|
||||
auto result = luaL_loadfilex(m_State, file_name.data(), "t");
|
||||
if (result != LUA_OK) // don't load binary chunks
|
||||
{
|
||||
auto error = lua_tostring(m_State, -1);
|
||||
LOGF(FATAL, "{}: {}", m_ModuleName, error);
|
||||
@@ -263,6 +264,46 @@ namespace YimMenu
|
||||
std::ranges::move(m_QueuedScriptCallbacks, std::back_inserter(m_ScriptCallbacks));
|
||||
m_QueuedScriptCallbacks.clear();
|
||||
}
|
||||
|
||||
void LuaScript::AddEventHandler(std::uint32_t event, int handler)
|
||||
{
|
||||
if (auto it = m_EventHandlers.find(event); it != m_EventHandlers.end())
|
||||
it->second.push_back(handler);
|
||||
else
|
||||
m_EventHandlers.emplace(event, std::vector{handler});
|
||||
}
|
||||
|
||||
bool LuaScript::DispatchEvent(std::uint32_t event, const DispatchEventCallback& add_arguments_cb, bool handle_result)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (auto it = m_EventHandlers.find(event); it != m_EventHandlers.end())
|
||||
{
|
||||
for (auto& handler : it->second)
|
||||
{
|
||||
lua_rawgeti(m_State, LUA_REGISTRYINDEX, handler);
|
||||
auto num_args = add_arguments_cb(m_State);
|
||||
|
||||
if (CallFunction(num_args, 1))
|
||||
{
|
||||
if (!lua_isnoneornil(m_State, -1))
|
||||
{
|
||||
if (lua_toboolean(m_State, -1) == false)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(m_State, 1);
|
||||
}
|
||||
|
||||
if (!result && handle_result)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void LuaScript::ScriptCallback::SetTimeToResume(int millis)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace YimMenu
|
||||
void SetTimeToResume(int millis);
|
||||
};
|
||||
|
||||
using DispatchEventCallback = std::function<int(lua_State* state)>;
|
||||
|
||||
private:
|
||||
LoadState m_LoadState = LoadState::EMPTY;
|
||||
lua_State* m_State = nullptr;
|
||||
@@ -40,6 +42,7 @@ namespace YimMenu
|
||||
std::vector<ScriptCallback> m_QueuedScriptCallbacks;
|
||||
bool m_RunningScriptCallbacks = false;
|
||||
ScriptCallback* m_CurrentlyExecutingCallback = nullptr;
|
||||
std::unordered_map<std::uint32_t, std::vector<int>> m_EventHandlers;
|
||||
|
||||
// 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);
|
||||
@@ -127,5 +130,8 @@ namespace YimMenu
|
||||
{
|
||||
return m_CurrentlyExecutingCallback;
|
||||
}
|
||||
|
||||
void AddEventHandler(std::uint32_t event, int handler);
|
||||
bool DispatchEvent(std::uint32_t event, const DispatchEventCallback& add_arguments_cb, bool handle_result = false);
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "core/scripting/LuaLibrary.hpp"
|
||||
#include "core/scripting/LuaScript.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
@@ -50,6 +51,17 @@ namespace YimMenu::Lua
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int RegisterEventHandler(lua_State* state)
|
||||
{
|
||||
auto event_name = luaL_checkstring(state, 1);
|
||||
luaL_checktype(state, 2, LUA_TFUNCTION);
|
||||
lua_pushvalue(state, 2);
|
||||
int handler = luaL_ref(state, LUA_REGISTRYINDEX);
|
||||
|
||||
LuaScript::GetScript(state).AddEventHandler(Joaat(event_name), handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void Register(lua_State* state) override
|
||||
{
|
||||
lua_newtable(state);
|
||||
@@ -63,6 +75,9 @@ namespace YimMenu::Lua
|
||||
lua_pushcfunction(state, IsInsideCallback);
|
||||
lua_setfield(state, -2, "is_inside_callback");
|
||||
|
||||
lua_pushcfunction(state, RegisterEventHandler);
|
||||
lua_setfield(state, -2, "register_event_handler");
|
||||
|
||||
lua_setglobal(state, "script");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace YimMenu::Submenus
|
||||
ImGui::Text("%s", selectedScript->GetName().data());
|
||||
|
||||
bool paused = selectedScript->GetLoadState() == LuaScript::LoadState::PAUSED;
|
||||
if (ImGui::Button(paused ? "Pause" : "Resume"))
|
||||
if (ImGui::Button(paused ? "Resume" : "Pause"))
|
||||
{
|
||||
if (paused)
|
||||
selectedScript->Resume();
|
||||
|
||||
@@ -5,11 +5,35 @@
|
||||
#include "types/script/globals/GPBD_FM_3.hpp"
|
||||
#include "types/script/globals/GlobalPlayerBD.hpp"
|
||||
#include "types/script/ScriptEvent.hpp"
|
||||
#include "core/scripting/LuaManager.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
namespace YimMenu::Hooks
|
||||
{
|
||||
static bool CheckLuaScripts(Player player, CScriptedGameEvent& event)
|
||||
{
|
||||
return LuaManager::DispatchEvent("menu.script_event_received"_J, [player, &event](lua_State* state)
|
||||
{
|
||||
// TODO: pass a Player instance
|
||||
lua_pushinteger(state, player.GetId());
|
||||
|
||||
lua_newtable(state);
|
||||
auto length = event.m_ArgsSize / 8;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
lua_pushinteger(state, i == 0 ? (ptrdiff_t)(int)event.m_Args[i] : event.m_Args[i]);
|
||||
lua_rawseti(state, -2, i + 1);
|
||||
}
|
||||
|
||||
return 2;
|
||||
}, true);
|
||||
}
|
||||
|
||||
bool Network::HandleScriptedGameEvent(Player player, CScriptedGameEvent& event)
|
||||
{
|
||||
if (!CheckLuaScripts(player, event))
|
||||
return false;
|
||||
|
||||
SCRIPT_EVENT* script_event = reinterpret_cast<SCRIPT_EVENT*>(event.m_Args);
|
||||
|
||||
switch (static_cast<ScriptEventIndex>(script_event->GetEventIndex()))
|
||||
|
||||
Reference in New Issue
Block a user