mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-11 09:48:08 +08:00
feat(lua): some more changes (incomplete)
This commit is contained in:
1 parent
1fcdc0f028
commit
b5a0bbbb3f
11 files changed
+407
-16
No files matched your search
@@ -0,0 +1,85 @@
|
||||
#include "LuaConfig.hpp"
|
||||
#include "core/filemgr/FileMgr.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
LuaConfig::LuaConfig(std::string_view script_name) :
|
||||
m_ConfigFile(FileMgr::GetProjectFile(std::string("./scripts/config/") + script_name.data() + ".json")),
|
||||
m_Dirty(false)
|
||||
{
|
||||
}
|
||||
|
||||
void LuaConfig::Load()
|
||||
{
|
||||
if (m_ConfigFile.Exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream stream(m_ConfigFile.Path());
|
||||
|
||||
nlohmann::json json;
|
||||
stream >> json;
|
||||
stream.close();
|
||||
|
||||
for (auto& [key, value] : json["item_states"].items())
|
||||
{
|
||||
auto as_uint = std::stol(key.c_str());
|
||||
m_ItemStates.emplace(as_uint, value);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOGF(FATAL, "Error loading config file {}: {}", m_ConfigFile.Path().filename().string(), e.what());
|
||||
std::filesystem::remove(m_ConfigFile.Path()); // TODO: this is probably a bad idea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LuaConfig::Update()
|
||||
{
|
||||
if (!m_Dirty)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
|
||||
nlohmann::json json;
|
||||
|
||||
json["item_states"] = nlohmann::json::object();
|
||||
|
||||
for (auto& [key, value] : m_ItemStates)
|
||||
{
|
||||
json[std::to_string(key)] = value;
|
||||
}
|
||||
|
||||
std::ofstream stream(m_ConfigFile.Path());
|
||||
stream << json;
|
||||
stream.close();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOGF(FATAL, "Error saving config file {}: {}", m_ConfigFile.Path().filename().string(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<nlohmann::json> LuaConfig::GetItemState(std::uint32_t hash)
|
||||
{
|
||||
// no need to lock when we're just reading from the array
|
||||
|
||||
if (auto it = m_ItemStates.find(hash); it != m_ItemStates.end())
|
||||
return it->second;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void LuaConfig::SetItemState(std::uint32_t hash, nlohmann::json value)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
|
||||
if (auto it = m_ItemStates.find(hash); it != m_ItemStates.end())
|
||||
it->second = value;
|
||||
else
|
||||
m_ItemStates.emplace(hash, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "core/filemgr/File.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class LuaConfig
|
||||
{
|
||||
bool m_Dirty;
|
||||
File m_ConfigFile;
|
||||
std::mutex m_Mutex;
|
||||
|
||||
std::unordered_map<std::uint32_t, nlohmann::json> m_ItemStates;
|
||||
|
||||
public:
|
||||
LuaConfig(std::string_view script_name);
|
||||
// call when a script is loaded
|
||||
void Load();
|
||||
// should be called every now and then (10s?) and before unload
|
||||
void Update();
|
||||
std::optional<nlohmann::json> GetItemState(std::uint32_t hash);
|
||||
void SetItemState(std::uint32_t hash, nlohmann::json value);
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "core/filemgr/FileMgr.hpp"
|
||||
#include "core/backend/ScriptMgr.hpp"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
#include "types/script/scrThread.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
@@ -129,10 +131,14 @@ namespace YimMenu
|
||||
}
|
||||
}
|
||||
|
||||
// 4) run tick coroutines
|
||||
for (auto& script : m_LoadedScripts)
|
||||
if (script->IsRunning())
|
||||
script->Tick();
|
||||
// don't run stuff while we're starting up
|
||||
if (rage::scrThread::GetRunningThread()->m_ScriptHash != "startup"_J)
|
||||
{
|
||||
// 4) run tick coroutines
|
||||
for (auto& script : m_LoadedScripts)
|
||||
if (script->IsRunning())
|
||||
script->Tick();
|
||||
}
|
||||
|
||||
ScriptMgr::Yield();
|
||||
}
|
||||
|
||||
@@ -145,7 +145,8 @@ namespace YimMenu
|
||||
|
||||
LuaScript::LuaScript(std::string_view file_name) :
|
||||
m_FileName(file_name),
|
||||
m_ModuleName(std::filesystem::path(file_name).filename().string())
|
||||
m_ModuleName(std::filesystem::path(file_name).filename().string()),
|
||||
m_Config(std::filesystem::path(file_name).stem().string())
|
||||
{
|
||||
m_Resources.resize(LuaManager::GetNumResourceTypes());
|
||||
m_State = luaL_newstate();
|
||||
@@ -219,8 +220,16 @@ namespace YimMenu
|
||||
return *script;
|
||||
}
|
||||
|
||||
void LuaScript::AddScriptCallback(int coro_handle)
|
||||
void LuaScript::AddScriptCallback(int func_handle)
|
||||
{
|
||||
lua_rawgeti(m_State, LUA_REGISTRYINDEX, func_handle);
|
||||
|
||||
lua_State* coro_state = lua_newthread(m_State);
|
||||
lua_pushvalue(m_State, 1); // xmove can only move from top of stack, so we have to push the function again even if it's already in the stack
|
||||
lua_xmove(m_State, coro_state, 1);
|
||||
|
||||
auto coro_handle = luaL_ref(m_State, LUA_REGISTRYINDEX);
|
||||
|
||||
ScriptCallback callback;
|
||||
callback.m_Coroutine = coro_handle;
|
||||
callback.m_LastYieldFromCode = false;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "lua.hpp"
|
||||
#include "LuaResource.hpp"
|
||||
#include "LuaConfig.hpp"
|
||||
#include "LuaUserInterface.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
@@ -45,6 +47,8 @@ namespace YimMenu
|
||||
ScriptCallback* m_CurrentlyExecutingCallback = nullptr;
|
||||
std::unordered_map<std::uint32_t, std::vector<int>> m_EventHandlers;
|
||||
std::vector<std::vector<std::shared_ptr<LuaResource>>> 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?
|
||||
LuaConfig m_Config;
|
||||
LuaUserInterface m_Interface;
|
||||
|
||||
// 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);
|
||||
@@ -110,8 +114,7 @@ namespace YimMenu
|
||||
// we're guaranteed to have a LuaScript for each lua_State, so we can return it as a reference
|
||||
static LuaScript& GetScript(lua_State* state);
|
||||
|
||||
|
||||
void AddScriptCallback(int coro_handle);
|
||||
void AddScriptCallback(int func_handle);
|
||||
|
||||
// must be called from a coroutine
|
||||
void Yield(lua_State* state, int millis = 0, bool from_code = true);
|
||||
@@ -130,5 +133,10 @@ namespace YimMenu
|
||||
void AddResource(std::shared_ptr<LuaResource>&& resource, int idx);
|
||||
int GetNumResourcesOfType(int type);
|
||||
std::vector<std::shared_ptr<LuaResource>>& GetAllResourcesOfType(int idx);
|
||||
|
||||
LuaUserInterface& GetUserInterface()
|
||||
{
|
||||
return m_Interface;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "LuaUserInterface.hpp"
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
UIElementBase::UIElementBase(LuaUserInterface& interface) :
|
||||
m_Interface(interface)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
something like
|
||||
|
||||
menu.set_menu_name("Test Interface Menu")
|
||||
root = menu.get_root()
|
||||
root:add_button("testbtn", "Test Button", function()
|
||||
notify.info("Test", "Hello")
|
||||
end)
|
||||
root:add_looped_checkbox("testbox", "Test Checkbox", function()
|
||||
-- on tick
|
||||
end, function()
|
||||
-- on enable
|
||||
end, function()
|
||||
-- on disable
|
||||
end, [should_serialize=true])
|
||||
x = root:add_group("Name", 3)
|
||||
y = root:add_tab_group
|
||||
*/
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class LuaUserInterface;
|
||||
}
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
class UIRoot;
|
||||
|
||||
class UIElementBase
|
||||
{
|
||||
LuaUserInterface& m_Interface;
|
||||
|
||||
public:
|
||||
UIElementBase(LuaUserInterface& _interface);
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
|
||||
class UIElementGroup : public UIElementBase
|
||||
{
|
||||
std::string m_Name;
|
||||
int m_NumElems; // num elements per row
|
||||
//std::vector<std::unique_ptr<UIElement>> m_Elements;
|
||||
|
||||
public:
|
||||
UIElementGroup(LuaUserInterface& _interface, std::string_view name, int num_elems = 7);
|
||||
virtual void Draw();
|
||||
};
|
||||
|
||||
class UIElement : public UIElementBase
|
||||
{
|
||||
std::string m_CmdString;
|
||||
std::uint32_t m_CmdHash;
|
||||
std::string m_Label;
|
||||
|
||||
public:
|
||||
virtual void Draw() override = 0;
|
||||
};
|
||||
|
||||
class UIElementWithState : public UIElement
|
||||
{
|
||||
bool m_ShouldSerialize;
|
||||
|
||||
public:
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
|
||||
class UIElementButton : public UIElement
|
||||
{
|
||||
int m_Function;
|
||||
|
||||
public:
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
|
||||
class UIButtonCheckbox : public UIElementWithState
|
||||
{
|
||||
bool m_Value;
|
||||
int m_OnChange;
|
||||
|
||||
public:
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
|
||||
class UIButtonIntSlider : public UIElementWithState
|
||||
{
|
||||
int m_Value;
|
||||
int m_OnChange;
|
||||
|
||||
public:
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
|
||||
class UIRoot
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class LuaUserInterface
|
||||
{
|
||||
std::mutex m_TickFunctionsLock;
|
||||
std::unordered_set<int> m_TickFunctions;
|
||||
std::deque<int> m_ThrottledCoroutines;
|
||||
std::chrono::system_clock::time_point m_LastThrotlledCoroutinePush;
|
||||
std::unordered_map<std::uint32_t, Lua::UIElement*> m_ElementsById;
|
||||
|
||||
public:
|
||||
void AddTickFunction(int func);
|
||||
void RemoveTickFunction(int func);
|
||||
void QueueCoroutine(int coro, bool immediate = false);
|
||||
|
||||
// must be called from the main thread
|
||||
void Tick();
|
||||
|
||||
// must be called from the DX thread
|
||||
void Draw();
|
||||
};
|
||||
}
|
||||
@@ -146,10 +146,10 @@ namespace YimMenu::Lua
|
||||
}
|
||||
|
||||
// luaL_checkstring automatically converts ints into strings, which we don't want
|
||||
inline const char* CheckStringSafe(lua_State* state, int index)
|
||||
inline const char* CheckStringSafe(lua_State* state, int index, size_t* size = nullptr)
|
||||
{
|
||||
luaL_checktype(state, index, LUA_TSTRING);
|
||||
return lua_tostring(state, index);
|
||||
return lua_tolstring(state, index, size);
|
||||
}
|
||||
|
||||
// lua doesn't even offer a check function for bools
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "core/scripting/LuaLibrary.hpp"
|
||||
#include "core/scripting/LuaScript.hpp"
|
||||
#include "core/scripting/LuaUtils.hpp"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
class Notify : LuaLibrary
|
||||
{
|
||||
using LuaLibrary::LuaLibrary;
|
||||
|
||||
static int Success(lua_State* state)
|
||||
{
|
||||
auto title = CheckStringSafe(state, 1);
|
||||
auto message = CheckStringSafe(state, 2);
|
||||
Notifications::Show(title, message, NotificationType::Success);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Info(lua_State* state)
|
||||
{
|
||||
auto title = CheckStringSafe(state, 1);
|
||||
auto message = CheckStringSafe(state, 2);
|
||||
Notifications::Show(title, message, NotificationType::Info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Warn(lua_State* state)
|
||||
{
|
||||
auto title = CheckStringSafe(state, 1);
|
||||
auto message = CheckStringSafe(state, 2);
|
||||
Notifications::Show(title, message, NotificationType::Warning);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Error(lua_State* state)
|
||||
{
|
||||
auto title = CheckStringSafe(state, 1);
|
||||
auto message = CheckStringSafe(state, 2);
|
||||
Notifications::Show(title, message, NotificationType::Error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void Register(lua_State* state) override
|
||||
{
|
||||
lua_newtable(state);
|
||||
SetFunction(state, Success, "success");
|
||||
SetFunction(state, Info, "info");
|
||||
SetFunction(state, Warn, "warn");
|
||||
SetFunction(state, Error, "error");
|
||||
lua_setglobal(state, "notify");
|
||||
}
|
||||
};
|
||||
|
||||
Notify _Notify;
|
||||
}
|
||||
@@ -22,12 +22,9 @@ namespace YimMenu::Lua
|
||||
|
||||
luaL_checktype(state, 1, LUA_TFUNCTION); // will throw error if a1 isn't a function. not sure what happens if you don't pass any parameters
|
||||
|
||||
lua_State* coro_state = lua_newthread(state);
|
||||
lua_pushvalue(state, 1); // xmove can only move from top of stack, so we have to push the function again even if it's already in the stack
|
||||
lua_xmove(state, coro_state, 1);
|
||||
|
||||
auto coro_handle = luaL_ref(state, LUA_REGISTRYINDEX);
|
||||
script.AddScriptCallback(coro_handle);
|
||||
auto func_handle = luaL_ref(state, LUA_REGISTRYINDEX);
|
||||
script.AddScriptCallback(func_handle);
|
||||
luaL_unref(state, LUA_REGISTRYINDEX, func_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "core/scripting/LuaLibrary.hpp"
|
||||
#include "core/scripting/LuaScript.hpp"
|
||||
#include "core/scripting/LuaUtils.hpp"
|
||||
#include "game/gta/Natives.hpp"
|
||||
#include "game/backend/Self.hpp"
|
||||
|
||||
namespace YimMenu::Lua
|
||||
{
|
||||
class Network : LuaLibrary
|
||||
{
|
||||
using LuaLibrary::LuaLibrary;
|
||||
|
||||
// trigger_script_event(2000, -1, "if", 2, 2.5)
|
||||
static int TriggerScriptEvent(lua_State* state)
|
||||
{
|
||||
auto hash = GetHashArgument(state, 1);
|
||||
int bits = luaL_checkinteger(state, 2);
|
||||
size_t param_length;
|
||||
auto param_str = CheckStringSafe(state, 3, ¶m_length);
|
||||
int64_t tse_args[40];
|
||||
tse_args[0] = hash;
|
||||
tse_args[1] = Self::GetPlayer().GetId();
|
||||
tse_args[2] = bits;
|
||||
|
||||
if (param_length >= 37)
|
||||
luaL_error(state, "too many arguments");
|
||||
|
||||
for (int i = 0; i < param_length; i++)
|
||||
{
|
||||
switch (param_str[i])
|
||||
{
|
||||
case 'i': // integer
|
||||
{
|
||||
int as_int = luaL_checkinteger(state, 4 + i);
|
||||
tse_args[3 + i] = *(int64_t*)&as_int;
|
||||
break;
|
||||
}
|
||||
case 'f': // float
|
||||
{
|
||||
float as_float = luaL_checknumber(state, 4 + i);
|
||||
tse_args[3 + i] = *(int64_t*)&as_float;
|
||||
break;
|
||||
}
|
||||
case 'l': // long
|
||||
{
|
||||
tse_args[3 + i] = luaL_checkinteger(state, 4 + i);
|
||||
break;
|
||||
}
|
||||
case 'h': // hash
|
||||
{
|
||||
std::uint32_t as_uint = GetHashArgument(state, 4 + i);
|
||||
tse_args[3 + i] = *(int64_t*)&as_uint;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
luaL_error(state, "unknown format specifier: '%c'", param_str[i]);
|
||||
}
|
||||
// TODO: add text label stuff
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT::_SEND_TU_SCRIPT_EVENT_NEW(1, tse_args, 3 + param_length, bits, hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void Register(lua_State* state) override
|
||||
{
|
||||
lua_newtable(state);
|
||||
SetFunction(state, TriggerScriptEvent, "trigger_script_event");
|
||||
lua_setglobal(state, "network");
|
||||
}
|
||||
};
|
||||
|
||||
Network _Network;
|
||||
}
|
||||
Reference in new issue
Block a user