Lua Scripting (#1334)
Closes #83 Fixes #1309 Fixes #1287 Fixes #1129 (actually fixed now)
This commit is contained in:
41
src/lua/bindings/command.hpp
Normal file
41
src/lua/bindings/command.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "backend/command.hpp"
|
||||
#include "backend/player_command.hpp"
|
||||
#include "network.hpp" // for convert_sequence
|
||||
|
||||
namespace lua::command
|
||||
{
|
||||
static void call(const std::string& command_name, std::optional<sol::table> _args)
|
||||
{
|
||||
auto args = convert_sequence<uint64_t>(_args.value_or(sol::table()));
|
||||
|
||||
auto command = big::command::get(rage::joaat(command_name));
|
||||
|
||||
if (command)
|
||||
command->call(args, {});
|
||||
}
|
||||
|
||||
static void call_player(int player_idx, const std::string& command_name, std::optional<sol::table> _args)
|
||||
{
|
||||
auto args = convert_sequence<uint64_t>(_args.value_or(sol::table()));
|
||||
|
||||
auto command = (big::player_command*)big::command::get(rage::joaat(command_name));
|
||||
|
||||
if (command)
|
||||
{
|
||||
auto player = big::g_player_service->get_by_id(player_idx);
|
||||
|
||||
if (player)
|
||||
{
|
||||
command->call(player, args, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["command"].get_or_create<sol::table>();
|
||||
ns["call"] = call;
|
||||
ns["call_player"] = call_player;
|
||||
}
|
||||
}
|
23
src/lua/bindings/event.hpp
Normal file
23
src/lua/bindings/event.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "fiber_pool.hpp"
|
||||
#include "lua/lua_module.hpp"
|
||||
#include "script_mgr.hpp"
|
||||
|
||||
namespace lua::event
|
||||
{
|
||||
static void register_handler(const std::string& name, sol::function func, sol::this_state state)
|
||||
{
|
||||
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
|
||||
|
||||
auto hash = rage::joaat(name);
|
||||
module->m_event_callbacks.emplace(hash, std::vector<sol::function>());
|
||||
module->m_event_callbacks[hash].push_back(func);
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["event"].get_or_create<sol::table>();
|
||||
ns["register_handler"] = register_handler;
|
||||
// TODO: triggering events through script?
|
||||
}
|
||||
}
|
53
src/lua/bindings/globals.hpp
Normal file
53
src/lua/bindings/globals.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include "memory.hpp"
|
||||
#include "script_global.hpp"
|
||||
|
||||
namespace lua::globals
|
||||
{
|
||||
static int get_int(int global)
|
||||
{
|
||||
return *big::script_global(global).as<int*>();
|
||||
}
|
||||
|
||||
static int get_float(int global)
|
||||
{
|
||||
return *big::script_global(global).as<float*>();
|
||||
}
|
||||
|
||||
static std::string get_string(int global)
|
||||
{
|
||||
return std::string(big::script_global(global).as<char*>());
|
||||
}
|
||||
|
||||
static void set_int(int global, int val)
|
||||
{
|
||||
*big::script_global(global).as<int*>() = val;
|
||||
}
|
||||
|
||||
static void set_float(int global, float val)
|
||||
{
|
||||
*big::script_global(global).as<float*>() = val;
|
||||
}
|
||||
|
||||
static void set_string(int global, const std::string& str, int max_length)
|
||||
{
|
||||
strncpy(big::script_global(global).as<char*>(), str.data(), max_length);
|
||||
}
|
||||
|
||||
static memory::pointer get_pointer(int global)
|
||||
{
|
||||
return memory::pointer((uint64_t)big::script_global(global).as<void*>());
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["globals"].get_or_create<sol::table>();
|
||||
ns["get_int"] = get_int;
|
||||
ns["get_float"] = get_float;
|
||||
ns["get_string"] = get_string;
|
||||
ns["set_int"] = set_int;
|
||||
ns["set_float"] = set_float;
|
||||
ns["set_string"] = set_string;
|
||||
ns["get_pointer"] = get_pointer;
|
||||
}
|
||||
}
|
12
src/lua/bindings/gui.cpp
Normal file
12
src/lua/bindings/gui.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "gui.hpp"
|
||||
|
||||
#include "../../gui.hpp"
|
||||
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
bool is_open()
|
||||
{
|
||||
return big::g_gui->is_open();
|
||||
}
|
||||
}
|
178
src/lua/bindings/gui.hpp
Normal file
178
src/lua/bindings/gui.hpp
Normal file
@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
#include "gui/button.hpp"
|
||||
#include "gui/checkbox.hpp"
|
||||
#include "gui/gui_element.hpp"
|
||||
#include "gui/input_float.hpp"
|
||||
#include "gui/input_int.hpp"
|
||||
#include "gui/input_string.hpp"
|
||||
#include "gui/sameline.hpp"
|
||||
#include "gui/separator.hpp"
|
||||
#include "gui/text.hpp"
|
||||
#include "lua/lua_module.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
static void add_element(lua_State* state, std::uint32_t hash, std::shared_ptr<lua::gui::gui_element> element)
|
||||
{
|
||||
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
|
||||
|
||||
if (!module->m_gui.contains(hash))
|
||||
module->m_gui[hash] = {};
|
||||
|
||||
module->m_gui[hash].push_back(std::move(element));
|
||||
}
|
||||
|
||||
class tab
|
||||
{
|
||||
rage::joaat_t m_tab_hash;
|
||||
|
||||
public:
|
||||
tab(rage::joaat_t hash) :
|
||||
m_tab_hash(hash)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::button> add_button(const std::string& name, sol::function callback, sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::button>(name, callback);
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::text> add_text(const std::string& name, sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::text>(name);
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::checkbox> add_checkbox(const std::string& name, sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::checkbox>(name);
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::sameline> add_sameline(sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::sameline>();
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::separator> add_separator(sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::separator>();
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::input_int> add_input_int(const std::string& name, sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::input_int>(name);
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::input_float> add_input_float(const std::string& name, sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::input_float>(name);
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
std::shared_ptr<lua::gui::input_string> add_input_string(const std::string& name, sol::this_state state)
|
||||
{
|
||||
auto element = std::make_shared<lua::gui::input_string>(name);
|
||||
add_element(state, m_tab_hash, element);
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
static tab get_tab(const std::string& tab_name)
|
||||
{
|
||||
return tab(rage::joaat(tab_name));
|
||||
}
|
||||
|
||||
static void show_message(const std::string& title, const std::string& message)
|
||||
{
|
||||
big::g_notification_service->push(title, message);
|
||||
}
|
||||
|
||||
static void show_warning(const std::string& title, const std::string& message)
|
||||
{
|
||||
big::g_notification_service->push_warning(title, message);
|
||||
}
|
||||
|
||||
static void show_error(const std::string& title, const std::string& message)
|
||||
{
|
||||
big::g_notification_service->push_error(title, message);
|
||||
}
|
||||
|
||||
bool is_open();
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["gui"].get_or_create<sol::table>();
|
||||
ns["get_tab"] = get_tab;
|
||||
ns["show_message"] = show_message;
|
||||
ns["show_warning"] = show_warning;
|
||||
ns["show_error"] = show_error;
|
||||
ns["is_open"] = is_open;
|
||||
|
||||
// clang-format off
|
||||
ns.new_usertype<lua::gui::button>("button",
|
||||
"set_text", &lua::gui::button::set_text,
|
||||
"get_text", &lua::gui::button::get_text
|
||||
);
|
||||
|
||||
ns.new_usertype<lua::gui::text>("text",
|
||||
"set_text", &lua::gui::text::set_text,
|
||||
"get_text", &lua::gui::text::get_text,
|
||||
"set_font", &lua::gui::text::set_font
|
||||
);
|
||||
|
||||
ns.new_usertype<lua::gui::checkbox>("checkbox",
|
||||
"set_text", &lua::gui::checkbox::set_text,
|
||||
"get_text", &lua::gui::checkbox::get_text,
|
||||
"is_enabled", &lua::gui::checkbox::is_enabled,
|
||||
"set_enabled", &lua::gui::checkbox::set_enabled
|
||||
);
|
||||
|
||||
ns.new_usertype<lua::gui::sameline>("sameline");
|
||||
ns.new_usertype<lua::gui::separator>("separator");
|
||||
|
||||
ns.new_usertype<lua::gui::input_int>("input_int",
|
||||
"set_text", &lua::gui::input_int::set_text,
|
||||
"get_text", &lua::gui::input_int::get_text,
|
||||
"get_value", &lua::gui::input_int::get_value,
|
||||
"set_value", &lua::gui::input_int::set_value
|
||||
);
|
||||
|
||||
ns.new_usertype<lua::gui::input_float>("input_float",
|
||||
"set_text", &lua::gui::input_float::set_text,
|
||||
"get_text", &lua::gui::input_float::get_text,
|
||||
"get_value", &lua::gui::input_float::get_value,
|
||||
"set_value", &lua::gui::input_float::set_value
|
||||
);
|
||||
|
||||
ns.new_usertype<lua::gui::input_string>("input_string",
|
||||
"set_text", &lua::gui::input_string::set_text,
|
||||
"get_text", &lua::gui::input_string::get_text,
|
||||
"get_value", &lua::gui::input_string::get_value,
|
||||
"set_value", &lua::gui::input_string::set_value
|
||||
);
|
||||
|
||||
ns.new_usertype<tab>("tab",
|
||||
"add_button", &tab::add_button,
|
||||
"add_text", &tab::add_text,
|
||||
"add_checkbox", &tab::add_checkbox,
|
||||
"add_sameline", &tab::add_sameline,
|
||||
"add_separator", &tab::add_separator,
|
||||
"add_input_int", &tab::add_input_int,
|
||||
"add_input_float", &tab::add_input_float,
|
||||
"add_input_string", &tab::add_input_string
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
}
|
19
src/lua/bindings/gui/base_text_element.cpp
Normal file
19
src/lua/bindings/gui/base_text_element.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "base_text_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
base_text_element::base_text_element(std::string text) :
|
||||
m_text(text)
|
||||
{
|
||||
}
|
||||
|
||||
void base_text_element::set_text(std::string new_text)
|
||||
{
|
||||
m_text = new_text;
|
||||
}
|
||||
|
||||
std::string base_text_element::get_text()
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
}
|
17
src/lua/bindings/gui/base_text_element.hpp
Normal file
17
src/lua/bindings/gui/base_text_element.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "gui_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class base_text_element : public gui_element
|
||||
{
|
||||
protected:
|
||||
std::string m_text;
|
||||
|
||||
public:
|
||||
base_text_element(std::string text);
|
||||
|
||||
void set_text(std::string new_text);
|
||||
std::string get_text();
|
||||
};
|
||||
}
|
34
src/lua/bindings/gui/button.cpp
Normal file
34
src/lua/bindings/gui/button.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "button.hpp"
|
||||
|
||||
#include "fiber_pool.hpp"
|
||||
#include "lua/lua_manager.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
button::button(std::string text, sol::function callback) :
|
||||
base_text_element(text),
|
||||
m_callback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
void button::draw()
|
||||
{
|
||||
if (ImGui::Button(m_text.data()))
|
||||
{
|
||||
if (m_execute_in_fiber_pool)
|
||||
{
|
||||
big::g_fiber_pool->queue_job([this] {
|
||||
auto res = m_callback();
|
||||
if (!res.valid())
|
||||
big::g_lua_manager->handle_error(res, res.lua_state());
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto res = m_callback();
|
||||
if (!res.valid())
|
||||
big::g_lua_manager->handle_error(res, res.lua_state());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
src/lua/bindings/gui/button.hpp
Normal file
17
src/lua/bindings/gui/button.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "base_text_element.hpp"
|
||||
#include "lua/sol.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class button : public base_text_element
|
||||
{
|
||||
sol::function m_callback;
|
||||
bool m_execute_in_fiber_pool = true;
|
||||
|
||||
public:
|
||||
button(std::string text, sol::function callback);
|
||||
|
||||
void draw() override;
|
||||
};
|
||||
}
|
24
src/lua/bindings/gui/checkbox.cpp
Normal file
24
src/lua/bindings/gui/checkbox.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "checkbox.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
checkbox::checkbox(std::string text) :
|
||||
base_text_element(text)
|
||||
{
|
||||
}
|
||||
|
||||
void checkbox::draw()
|
||||
{
|
||||
ImGui::Checkbox(m_text.data(), &m_enabled);
|
||||
}
|
||||
|
||||
bool checkbox::is_enabled()
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void checkbox::set_enabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
}
|
||||
}
|
18
src/lua/bindings/gui/checkbox.hpp
Normal file
18
src/lua/bindings/gui/checkbox.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "base_text_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class checkbox : public base_text_element
|
||||
{
|
||||
bool m_enabled = false;
|
||||
|
||||
public:
|
||||
checkbox(std::string text);
|
||||
|
||||
void draw() override;
|
||||
|
||||
bool is_enabled();
|
||||
void set_enabled(bool enabled);
|
||||
};
|
||||
}
|
10
src/lua/bindings/gui/gui_element.hpp
Normal file
10
src/lua/bindings/gui/gui_element.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class gui_element
|
||||
{
|
||||
public:
|
||||
virtual void draw() = 0;
|
||||
};
|
||||
}
|
24
src/lua/bindings/gui/input_float.cpp
Normal file
24
src/lua/bindings/gui/input_float.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "input_float.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
input_float::input_float(std::string text) :
|
||||
base_text_element(text)
|
||||
{
|
||||
}
|
||||
|
||||
void input_float::draw()
|
||||
{
|
||||
ImGui::InputFloat(m_text.c_str(), &m_value);
|
||||
}
|
||||
|
||||
int input_float::get_value()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void input_float::set_value(float val)
|
||||
{
|
||||
m_value = val;
|
||||
}
|
||||
}
|
19
src/lua/bindings/gui/input_float.hpp
Normal file
19
src/lua/bindings/gui/input_float.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "base_text_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class input_float : public base_text_element
|
||||
{
|
||||
bool m_enabled = false;
|
||||
float m_value = 0;
|
||||
|
||||
public:
|
||||
input_float(std::string text);
|
||||
|
||||
void draw() override;
|
||||
|
||||
int get_value();
|
||||
void set_value(float val);
|
||||
};
|
||||
}
|
24
src/lua/bindings/gui/input_int.cpp
Normal file
24
src/lua/bindings/gui/input_int.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "input_int.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
input_int::input_int(std::string text) :
|
||||
base_text_element(text)
|
||||
{
|
||||
}
|
||||
|
||||
void input_int::draw()
|
||||
{
|
||||
ImGui::InputInt(m_text.c_str(), &m_value);
|
||||
}
|
||||
|
||||
int input_int::get_value()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void input_int::set_value(int val)
|
||||
{
|
||||
m_value = val;
|
||||
}
|
||||
}
|
19
src/lua/bindings/gui/input_int.hpp
Normal file
19
src/lua/bindings/gui/input_int.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "base_text_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class input_int : public base_text_element
|
||||
{
|
||||
bool m_enabled = false;
|
||||
int m_value = 0;
|
||||
|
||||
public:
|
||||
input_int(std::string text);
|
||||
|
||||
void draw() override;
|
||||
|
||||
int get_value();
|
||||
void set_value(int val);
|
||||
};
|
||||
}
|
24
src/lua/bindings/gui/input_string.cpp
Normal file
24
src/lua/bindings/gui/input_string.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "input_string.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
input_string::input_string(std::string text) :
|
||||
base_text_element(text)
|
||||
{
|
||||
}
|
||||
|
||||
void input_string::draw()
|
||||
{
|
||||
ImGui::InputText(m_text.c_str(), m_value, sizeof(m_value));
|
||||
}
|
||||
|
||||
std::string input_string::get_value()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void input_string::set_value(std::string val)
|
||||
{
|
||||
strncpy(m_value, val.c_str(), sizeof(m_value));
|
||||
}
|
||||
}
|
19
src/lua/bindings/gui/input_string.hpp
Normal file
19
src/lua/bindings/gui/input_string.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "base_text_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class input_string : public base_text_element
|
||||
{
|
||||
bool m_enabled = false;
|
||||
char m_value[255] = {};
|
||||
|
||||
public:
|
||||
input_string(std::string text);
|
||||
|
||||
void draw() override;
|
||||
|
||||
std::string get_value();
|
||||
void set_value(std::string val);
|
||||
};
|
||||
}
|
9
src/lua/bindings/gui/sameline.cpp
Normal file
9
src/lua/bindings/gui/sameline.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "sameline.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
void sameline::draw()
|
||||
{
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
13
src/lua/bindings/gui/sameline.hpp
Normal file
13
src/lua/bindings/gui/sameline.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "gui_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class sameline : public gui_element
|
||||
{
|
||||
public:
|
||||
sameline(){};
|
||||
|
||||
void draw() override;
|
||||
};
|
||||
}
|
9
src/lua/bindings/gui/separator.cpp
Normal file
9
src/lua/bindings/gui/separator.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "separator.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
void separator::draw()
|
||||
{
|
||||
ImGui::Separator();
|
||||
}
|
||||
}
|
13
src/lua/bindings/gui/separator.hpp
Normal file
13
src/lua/bindings/gui/separator.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "gui_element.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class separator : public gui_element
|
||||
{
|
||||
public:
|
||||
separator(){};
|
||||
|
||||
void draw() override;
|
||||
};
|
||||
}
|
32
src/lua/bindings/gui/text.cpp
Normal file
32
src/lua/bindings/gui/text.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "text.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
text::text(std::string text) :
|
||||
base_text_element(text)
|
||||
{
|
||||
}
|
||||
|
||||
void text::draw()
|
||||
{
|
||||
if (m_font)
|
||||
ImGui::PushFont(m_font);
|
||||
ImGui::Text(m_text.data());
|
||||
if (m_font)
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
void text::set_font(std::string font)
|
||||
{
|
||||
auto hash = rage::joaat(font);
|
||||
|
||||
switch (hash)
|
||||
{
|
||||
case RAGE_JOAAT("title"): m_font = big::g.window.font_title; break;
|
||||
case RAGE_JOAAT("subtitle"): m_font = big::g.window.font_sub_title; break;
|
||||
case RAGE_JOAAT("small"): m_font = big::g.window.font_small; break;
|
||||
case RAGE_JOAAT("icon"): m_font = big::g.window.font_icon; break;
|
||||
default: m_font = nullptr; break;
|
||||
}
|
||||
}
|
||||
}
|
17
src/lua/bindings/gui/text.hpp
Normal file
17
src/lua/bindings/gui/text.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "base_text_element.hpp"
|
||||
#include "lua/sol.hpp"
|
||||
|
||||
namespace lua::gui
|
||||
{
|
||||
class text : public base_text_element
|
||||
{
|
||||
ImFont* m_font = nullptr;
|
||||
|
||||
public:
|
||||
text(std::string text);
|
||||
|
||||
void draw() override;
|
||||
void set_font(std::string font);
|
||||
};
|
||||
}
|
53
src/lua/bindings/locals.hpp
Normal file
53
src/lua/bindings/locals.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include "gta_util.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "script_local.hpp"
|
||||
|
||||
namespace lua::locals
|
||||
{
|
||||
template<typename T>
|
||||
inline T get(const std::string& script, int index)
|
||||
{
|
||||
static std::remove_pointer_t<T> null{};
|
||||
auto thread = big::gta_util::find_script_thread(rage::joaat(script));
|
||||
if (thread)
|
||||
return big::script_local(thread->m_stack, index).as<T>();
|
||||
|
||||
return &null;
|
||||
}
|
||||
|
||||
static int get_int(const std::string& script, int index)
|
||||
{
|
||||
return *get<int*>(script, index);
|
||||
}
|
||||
|
||||
static int get_float(const std::string& script, int index)
|
||||
{
|
||||
return *get<float*>(script, index);
|
||||
}
|
||||
|
||||
static void set_int(const std::string& script, int index, int val)
|
||||
{
|
||||
*get<int*>(script, index) = val;
|
||||
}
|
||||
|
||||
static void set_float(const std::string& script, int index, float val)
|
||||
{
|
||||
*get<float*>(script, index) = val;
|
||||
}
|
||||
|
||||
static memory::pointer get_pointer(const std::string& script, int index)
|
||||
{
|
||||
return memory::pointer((uint64_t)get<int*>(script, index));
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["locals"].get_or_create<sol::table>();
|
||||
ns["get_int"] = get_int;
|
||||
ns["get_float"] = get_float;
|
||||
ns["set_int"] = set_int;
|
||||
ns["set_float"] = set_float;
|
||||
ns["get_pointer"] = get_pointer;
|
||||
}
|
||||
}
|
27
src/lua/bindings/log.hpp
Normal file
27
src/lua/bindings/log.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
namespace lua::log
|
||||
{
|
||||
static void info(const std::string& data, sol::this_state state)
|
||||
{
|
||||
LOG(INFO) << sol::state_view(state)["!module_name"].get<std::string>() << ": " << data;
|
||||
}
|
||||
|
||||
static void warning(const std::string& data, sol::this_state state)
|
||||
{
|
||||
LOG(WARNING) << sol::state_view(state)["!module_name"].get<std::string>() << ": " << data;
|
||||
}
|
||||
|
||||
static void debug(const std::string& data, sol::this_state state)
|
||||
{
|
||||
LOG(VERBOSE) << sol::state_view(state)["!module_name"].get<std::string>() << ": " << data;
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["log"].get_or_create<sol::table>();
|
||||
ns["info"] = info;
|
||||
ns["warning"] = warning;
|
||||
ns["debug"] = debug;
|
||||
}
|
||||
}
|
44
src/lua/bindings/memory.cpp
Normal file
44
src/lua/bindings/memory.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "memory.hpp"
|
||||
|
||||
#include "memory/module.hpp"
|
||||
#include "memory/pattern.hpp"
|
||||
#include "pointers.hpp"
|
||||
|
||||
namespace lua::memory
|
||||
{
|
||||
pointer scan_pattern(const std::string& pattern)
|
||||
{
|
||||
return pointer(::memory::module("GTA5.exe").scan(::memory::pattern(pattern)).value().as<uint64_t>());
|
||||
}
|
||||
|
||||
pointer handle_to_ptr(int entity)
|
||||
{
|
||||
auto ptr = big::g_pointers->m_gta.m_handle_to_ptr(entity);
|
||||
return pointer((uint64_t)ptr);
|
||||
}
|
||||
|
||||
int ptr_to_handle(pointer mem_addr)
|
||||
{
|
||||
if (mem_addr.is_null())
|
||||
return 0;
|
||||
|
||||
return big::g_pointers->m_gta.m_ptr_to_handle((void*)mem_addr.get_address());
|
||||
}
|
||||
|
||||
pointer allocate(int size, sol::this_state state)
|
||||
{
|
||||
void* mem = new uint8_t[](size);
|
||||
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
|
||||
module->m_allocated_memory.push_back(mem);
|
||||
return pointer((uint64_t)mem);
|
||||
}
|
||||
|
||||
void free(pointer ptr, sol::this_state state)
|
||||
{
|
||||
delete[] (void*)ptr.get_address();
|
||||
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
|
||||
std::erase_if(module->m_allocated_memory, [ptr](void* addr) {
|
||||
return ptr.get_address() == (uint64_t)addr;
|
||||
});
|
||||
}
|
||||
}
|
142
src/lua/bindings/memory.hpp
Normal file
142
src/lua/bindings/memory.hpp
Normal file
@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
#include "lua/lua_module.hpp"
|
||||
#include "lua/lua_patch.hpp"
|
||||
#include "lua/sol.hpp"
|
||||
#include "memory/byte_patch.hpp"
|
||||
|
||||
namespace lua::memory
|
||||
{
|
||||
struct pointer
|
||||
{
|
||||
private:
|
||||
std::uint64_t m_address;
|
||||
|
||||
public:
|
||||
explicit pointer(std::uint64_t address) :
|
||||
m_address(address)
|
||||
{
|
||||
}
|
||||
|
||||
explicit pointer() :
|
||||
m_address(0)
|
||||
{
|
||||
}
|
||||
|
||||
pointer add(uint64_t offset)
|
||||
{
|
||||
return pointer(m_address + offset);
|
||||
}
|
||||
|
||||
pointer sub(uint64_t offset)
|
||||
{
|
||||
return pointer(m_address - offset);
|
||||
}
|
||||
|
||||
pointer rip()
|
||||
{
|
||||
return add(*(std::int32_t*)m_address).add(4);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get()
|
||||
{
|
||||
return *(T*)m_address;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(T value)
|
||||
{
|
||||
*(T*)m_address = value;
|
||||
}
|
||||
|
||||
std::string get_string()
|
||||
{
|
||||
return std::string((char*)m_address);
|
||||
}
|
||||
|
||||
void set_string(const std::string& string, int max_length)
|
||||
{
|
||||
strncpy((char*)m_address, string.data(), max_length);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
big::lua_patch* patch(T value, sol::this_state state)
|
||||
{
|
||||
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
|
||||
auto patch = std::make_shared<big::lua_patch>(::memory::byte_patch::make((T*)m_address, value).get());
|
||||
auto raw = patch.get();
|
||||
module->m_registered_patches.push_back(std::move(patch));
|
||||
return raw;
|
||||
}
|
||||
|
||||
bool is_null()
|
||||
{
|
||||
return m_address == 0;
|
||||
}
|
||||
|
||||
bool is_valid()
|
||||
{
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
pointer deref()
|
||||
{
|
||||
return pointer(*(uint64_t*)m_address);
|
||||
}
|
||||
|
||||
uint64_t get_address() const
|
||||
{
|
||||
return m_address;
|
||||
}
|
||||
};
|
||||
|
||||
pointer scan_pattern(const std::string& pattern);
|
||||
pointer handle_to_ptr(int entity);
|
||||
int ptr_to_handle(pointer mem_addr);
|
||||
pointer allocate(int size, sol::this_state state);
|
||||
void free(pointer ptr, sol::this_state state);
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["memory"].get_or_create<sol::table>();
|
||||
|
||||
// clang-format off
|
||||
ns.new_usertype<pointer>("pointer", sol::constructors<pointer(std::uint64_t)>(),
|
||||
"add", &pointer::add,
|
||||
"sub", &pointer::sub,
|
||||
"rip", &pointer::rip,
|
||||
"get_byte", &pointer::get<uint8_t>,
|
||||
"get_word", &pointer::get<uint16_t>,
|
||||
"get_dword", &pointer::get<uint32_t>,
|
||||
"get_float", &pointer::get<float>,
|
||||
"get_qword", &pointer::get<uint64_t>,
|
||||
"get_string", &pointer::get_string,
|
||||
"set_byte", &pointer::set<uint8_t>,
|
||||
"set_word", &pointer::set<uint16_t>,
|
||||
"set_dword", &pointer::set<uint32_t>,
|
||||
"set_float", &pointer::set<float>,
|
||||
"set_qword", &pointer::set<uint64_t>,
|
||||
"set_string", &pointer::set_string,
|
||||
"patch_byte", &pointer::patch<uint8_t>,
|
||||
"patch_word", &pointer::patch<uint16_t>,
|
||||
"patch_dword", &pointer::patch<uint32_t>,
|
||||
"patch_qword", &pointer::patch<uint64_t>,
|
||||
"is_null", &pointer::is_null,
|
||||
"is_valid", &pointer::is_valid,
|
||||
"deref", &pointer::deref,
|
||||
"get_address", &pointer::get_address
|
||||
);
|
||||
|
||||
ns.new_usertype<big::lua_patch>("patch", sol::no_constructor,
|
||||
"apply", &big::lua_patch::apply,
|
||||
"restore", &big::lua_patch::restore
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
ns["scan_pattern"] = scan_pattern;
|
||||
ns["handle_to_ptr"] = handle_to_ptr;
|
||||
ns["ptr_to_handle"] = ptr_to_handle;
|
||||
ns["allocate"] = allocate;
|
||||
ns["free"] = free;
|
||||
}
|
||||
}
|
78
src/lua/bindings/native.hpp
Normal file
78
src/lua/bindings/native.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include "invoker.hpp"
|
||||
#include "lua/lua_module.hpp"
|
||||
#include "lua/natives/natives_data.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace lua::native
|
||||
{
|
||||
template<typename T>
|
||||
T invoke(sol::variadic_args args)
|
||||
{
|
||||
big::g_native_invoker.begin_call();
|
||||
|
||||
for (int i = 1; i < args.size(); i++)
|
||||
{
|
||||
if (args[i].is<memory::pointer>())
|
||||
big::g_native_invoker.push_arg(args[i].get<memory::pointer>().get_address());
|
||||
else if (args[i].is<int>())
|
||||
big::g_native_invoker.push_arg(args[i].get<int>());
|
||||
else if (args[i].is<float>())
|
||||
big::g_native_invoker.push_arg(args[i].get<float>());
|
||||
else if (args[i].is<bool>())
|
||||
big::g_native_invoker.push_arg(args[i].get<bool>());
|
||||
else if (args[i].is<const char*>())
|
||||
big::g_native_invoker.push_arg(args[i].get<const char*>());
|
||||
else if (args[i].is<rage::fvector3>())
|
||||
{
|
||||
auto vec = args[i].get<rage::fvector3>();
|
||||
big::g_native_invoker.push_arg(vec.x);
|
||||
big::g_native_invoker.push_arg(vec.y);
|
||||
big::g_native_invoker.push_arg(vec.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(FATAL) << "Unhandled parameter";
|
||||
return T();
|
||||
}
|
||||
}
|
||||
|
||||
big::g_native_invoker.end_call(args[0].as<std::uint64_t>());
|
||||
|
||||
if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
return std::string(big::g_native_invoker.get_return_value<const char*>());
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, rage::fvector3>)
|
||||
{
|
||||
auto& vec = big::g_native_invoker.get_return_value<Vector3>();
|
||||
return {vec.x, vec.y, vec.z};
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, memory::pointer>)
|
||||
{
|
||||
return memory::pointer(big::g_native_invoker.get_return_value<std::uint64_t>());
|
||||
}
|
||||
else if constexpr (!std::is_void_v<T>)
|
||||
{
|
||||
return big::g_native_invoker.get_return_value<T>();
|
||||
}
|
||||
}
|
||||
|
||||
void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["_natives"].get_or_create<sol::table>();
|
||||
ns["invoke_void"] = invoke<void>;
|
||||
ns["invoke_int"] = invoke<int>;
|
||||
ns["invoke_float"] = invoke<float>;
|
||||
ns["invoke_bool"] = invoke<bool>;
|
||||
ns["invoke_str"] = invoke<std::string>;
|
||||
ns["invoke_vec3"] = invoke<rage::fvector3>;
|
||||
ns["invoke_ptr"] = invoke<memory::pointer>;
|
||||
|
||||
auto result = state.load_buffer(natives_data, natives_size);
|
||||
if (!result.valid())
|
||||
LOG(FATAL) << "Failed to load natives data: " << result.get<sol::error>().what();
|
||||
|
||||
result();
|
||||
}
|
||||
}
|
97
src/lua/bindings/network.cpp
Normal file
97
src/lua/bindings/network.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "network.hpp"
|
||||
|
||||
#include "hooking.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "services/player_database/player_database_service.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/scripts.hpp"
|
||||
#include "util/system.hpp"
|
||||
#include "util/teleport.hpp"
|
||||
|
||||
namespace lua::network
|
||||
{
|
||||
void trigger_script_event(int bitset, sol::table _args)
|
||||
{
|
||||
auto args = convert_sequence<int32_t>(_args);
|
||||
|
||||
if (args.size() >= 1)
|
||||
args[1] = self::id;
|
||||
|
||||
std::vector<std::int64_t> actual_args;
|
||||
|
||||
for (auto arg : args)
|
||||
actual_args.push_back((uint32_t)arg);
|
||||
|
||||
big::g_pointers->m_gta.m_trigger_script_event(1, actual_args.data(), actual_args.size(), bitset);
|
||||
}
|
||||
|
||||
void give_pickup_rewards(int player, int reward)
|
||||
{
|
||||
big::g_pointers->m_gta.m_give_pickup_rewards(1 << player, reward);
|
||||
}
|
||||
|
||||
void set_player_coords(int player_idx, float x, float y, float z)
|
||||
{
|
||||
if (auto player = big::g_player_service->get_by_id(player_idx))
|
||||
big::teleport::teleport_player_to_coords(player, {x, y, z});
|
||||
}
|
||||
|
||||
void set_all_player_coords(float x, float y, float z)
|
||||
{
|
||||
for (auto& player : big::g_player_service->players())
|
||||
big::g_fiber_pool->queue_job([player, x, y, z]() {
|
||||
big::teleport::teleport_player_to_coords(player.second, {x, y, z});
|
||||
});
|
||||
}
|
||||
|
||||
int get_selected_player()
|
||||
{
|
||||
if (big::g_player_service->get_selected()->is_valid())
|
||||
return big::g_player_service->get_selected()->id();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_selected_database_player_rockstar_id()
|
||||
{
|
||||
if (auto pers = big::g_player_database_service->get_selected())
|
||||
return pers->rockstar_id;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void flag_player_as_modder(int player_idx)
|
||||
{
|
||||
if (auto player = big::g_player_service->get_by_id(player_idx))
|
||||
{
|
||||
auto pers = big::g_player_database_service->get_or_create_player(player);
|
||||
pers->is_modder = true;
|
||||
big::g_player_database_service->save();
|
||||
player->is_modder = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_player_flagged_as_modder(int player_idx)
|
||||
{
|
||||
if (auto player = big::g_player_service->get_by_id(player_idx))
|
||||
return player->is_modder;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void force_script_host(const std::string& script_name)
|
||||
{
|
||||
big::scripts::force_host(rage::joaat(script_name));
|
||||
}
|
||||
|
||||
void send_chat_message(const std::string& msg, bool team_only)
|
||||
{
|
||||
big::g_fiber_pool->queue_job([msg, team_only] {
|
||||
if (big::g_hooking->get_original<big::hooks::send_chat_message>()(*big::g_pointers->m_gta.m_send_chat_ptr,
|
||||
big::g_player_service->get_self()->get_net_data(),
|
||||
(char*)msg.c_str(),
|
||||
team_only))
|
||||
big::notify::draw_chat((char*)msg.data(), big::g_player_service->get_self()->get_name(), team_only);
|
||||
});
|
||||
}
|
||||
}
|
48
src/lua/bindings/network.hpp
Normal file
48
src/lua/bindings/network.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "lua/sol.hpp"
|
||||
|
||||
// https://stackoverflow.com/a/40777268
|
||||
/**
|
||||
* Convert a Lua sequence into a C++ vector
|
||||
* Throw exception on errors or wrong types
|
||||
*/
|
||||
template<typename elementType>
|
||||
inline std::vector<elementType> convert_sequence(sol::table t)
|
||||
{
|
||||
std::size_t sz = t.size();
|
||||
std::vector<elementType> res(sz);
|
||||
for (int i = 1; i <= sz; i++)
|
||||
{
|
||||
res[i - 1] = t[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace lua::network
|
||||
{
|
||||
void trigger_script_event(int bitset, sol::table _args);
|
||||
void give_pickup_rewards(int player, int reward);
|
||||
void set_player_coords(int player_idx, float x, float y, float z);
|
||||
void set_all_player_coords(float x, float y, float z);
|
||||
int get_selected_player();
|
||||
int get_selected_database_player_rockstar_id();
|
||||
void flag_player_as_modder(int player_idx);
|
||||
bool is_player_flagged_as_modder(int player_idx);
|
||||
void force_script_host(const std::string& script_name);
|
||||
void send_chat_message(const std::string& msg, bool team_only);
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["network"].get_or_create<sol::table>();
|
||||
ns["trigger_script_event"] = trigger_script_event;
|
||||
ns["give_pickup_rewards"] = give_pickup_rewards;
|
||||
ns["set_player_coords"] = set_player_coords;
|
||||
ns["set_all_player_coords"] = set_all_player_coords;
|
||||
ns["get_selected_player"] = get_selected_player;
|
||||
ns["get_selected_database_player_rockstar_id"] = get_selected_database_player_rockstar_id;
|
||||
ns["flag_player_as_modder"] = flag_player_as_modder;
|
||||
ns["is_player_flagged_as_modder"] = is_player_flagged_as_modder;
|
||||
ns["force_script_host"] = force_script_host;
|
||||
ns["send_chat_message"] = send_chat_message;
|
||||
}
|
||||
}
|
54
src/lua/bindings/script.hpp
Normal file
54
src/lua/bindings/script.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "fiber_pool.hpp"
|
||||
#include "lua/lua_manager.hpp"
|
||||
#include "lua/lua_module.hpp"
|
||||
#include "script_mgr.hpp"
|
||||
|
||||
namespace lua::script
|
||||
{
|
||||
static void register_looped(const std::string& name, sol::function func, sol::this_state state)
|
||||
{
|
||||
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
|
||||
|
||||
module->m_registered_scripts.push_back(big::g_script_mgr.add_script(std::make_unique<big::script>(
|
||||
[func] {
|
||||
while (big::g_running)
|
||||
{
|
||||
auto res = func();
|
||||
if (!res.valid())
|
||||
big::g_lua_manager->handle_error(res, res.lua_state());
|
||||
|
||||
big::script::get_current()->yield();
|
||||
}
|
||||
},
|
||||
name)));
|
||||
}
|
||||
|
||||
static void run_in_fiber(sol::function func)
|
||||
{
|
||||
big::g_fiber_pool->queue_job([func] {
|
||||
auto res = func();
|
||||
if (!res.valid())
|
||||
big::g_lua_manager->handle_error(res, res.lua_state());
|
||||
});
|
||||
}
|
||||
|
||||
static void yield()
|
||||
{
|
||||
big::script::get_current()->yield();
|
||||
}
|
||||
|
||||
static void sleep(int ms)
|
||||
{
|
||||
big::script::get_current()->yield(std::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["script"].get_or_create<sol::table>();
|
||||
ns["register_looped"] = register_looped;
|
||||
ns["run_in_fiber"] = run_in_fiber;
|
||||
ns["yield"] = yield;
|
||||
ns["sleep"] = sleep;
|
||||
}
|
||||
}
|
31
src/lua/bindings/tunables.hpp
Normal file
31
src/lua/bindings/tunables.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "services/tunables/tunables_service.hpp"
|
||||
|
||||
namespace lua::tunables
|
||||
{
|
||||
template<typename T>
|
||||
static T get(const std::string tunable_name)
|
||||
{
|
||||
if (auto tunable = big::g_tunables_service->get_tunable<T*>(rage::joaat(tunable_name)))
|
||||
return *tunable;
|
||||
|
||||
return T();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void set(const std::string tunable_name, T val)
|
||||
{
|
||||
big::g_tunables_service->set_tunable<T>(rage::joaat(tunable_name), val);
|
||||
}
|
||||
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
auto ns = state["tunables"].get_or_create<sol::table>();
|
||||
ns["get_int"] = get<int>;
|
||||
ns["get_float"] = get<float>;
|
||||
ns["get_bool"] = get<bool>;
|
||||
ns["set_int"] = set<int>;
|
||||
ns["set_float"] = set<float>;
|
||||
ns["set_bool"] = set<bool>;
|
||||
}
|
||||
}
|
15
src/lua/bindings/vector.hpp
Normal file
15
src/lua/bindings/vector.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace lua::vector
|
||||
{
|
||||
static void bind(sol::state& state)
|
||||
{
|
||||
// clang-format off
|
||||
state.new_usertype<rage::fvector3>("vec3", sol::constructors<rage::fvector3(float, float, float)>(),
|
||||
"x", &rage::fvector3::x,
|
||||
"y", &rage::fvector3::y,
|
||||
"z", &rage::fvector3::z
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user