Support calling built-in LUI functions
This commit is contained in:
parent
cb7517e583
commit
e5e1319543
@ -56,7 +56,6 @@ namespace scripting
|
||||
|
||||
void player_spawn_stub(const game::gentity_s* player)
|
||||
{
|
||||
command::execute("reloadmenus");
|
||||
player_spawn_hook.invoke<void>(player);
|
||||
lua::engine::start();
|
||||
}
|
||||
|
@ -17,20 +17,110 @@
|
||||
#include "game/ui_scripting/lua/engine.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, game::hks::lua_function>> libs;
|
||||
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour hksi_open_lib_hook;
|
||||
utils::hook::detour hksi_lual_error_hook;
|
||||
utils::hook::detour hks_start_hook;
|
||||
utils::hook::detour hks_shutdown_hook;
|
||||
|
||||
bool error_hook_enabled = false;
|
||||
|
||||
void* hksi_open_lib_stub(game::hks::lua_State* s, const char* libname, game::hks::luaL_Reg* l)
|
||||
{
|
||||
if (libname)
|
||||
{
|
||||
libs[libname] = {};
|
||||
for (auto i = l; i->name; ++i)
|
||||
{
|
||||
libs[libname][i->name] = i->function;
|
||||
}
|
||||
}
|
||||
|
||||
return hksi_open_lib_hook.invoke<void*>(s, libname, l);
|
||||
}
|
||||
|
||||
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
||||
{
|
||||
char va_buffer[0x200] = { 0 };
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsprintf_s(va_buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
const auto formatted = std::string(va_buffer);
|
||||
|
||||
if (!error_hook_enabled)
|
||||
{
|
||||
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
void* hks_start_stub(char a1)
|
||||
{
|
||||
const auto _ = gsl::finally([]()
|
||||
{
|
||||
ui_scripting::lua::engine::start();
|
||||
});
|
||||
|
||||
libs = {};
|
||||
return hks_start_hook.invoke<void*>(a1);
|
||||
}
|
||||
|
||||
void hks_shutdown_stub()
|
||||
{
|
||||
printf("shutdown\n");
|
||||
ui_scripting::lua::engine::stop();
|
||||
hks_shutdown_hook.invoke<void*>();
|
||||
}
|
||||
}
|
||||
|
||||
void enable_error_hook()
|
||||
{
|
||||
error_hook_enabled = true;
|
||||
}
|
||||
|
||||
void disable_error_hook()
|
||||
{
|
||||
error_hook_enabled = false;
|
||||
}
|
||||
|
||||
game::hks::lua_function find_function(const std::string& name)
|
||||
{
|
||||
const auto lower = utils::string::to_lower(name);
|
||||
|
||||
for (const auto lib : libs)
|
||||
{
|
||||
for (const auto func : lib.second)
|
||||
{
|
||||
const auto lower_ = utils::string::to_lower(func.first);
|
||||
if (lower == lower_)
|
||||
{
|
||||
return func.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
scheduler::once([]()
|
||||
{
|
||||
ui_scripting::lua::engine::start();
|
||||
}, scheduler::pipeline::renderer);
|
||||
|
||||
scheduler::loop([]()
|
||||
{
|
||||
ui_scripting::lua::engine::run_frame();
|
||||
@ -64,6 +154,11 @@ namespace ui_scripting
|
||||
ui_scripting::lua::engine::close_menu(name);
|
||||
}, scheduler::pipeline::renderer);
|
||||
});
|
||||
|
||||
hks_start_hook.create(game::base_address + 0x328BE0, hks_start_stub);
|
||||
hks_shutdown_hook.create(game::base_address + 0x3203B0, hks_shutdown_stub);
|
||||
hksi_open_lib_hook.create(game::base_address + 0x2E4530, hksi_open_lib_stub);
|
||||
hksi_lual_error_hook.create(game::base_address + 0x2E3E40, hksi_lual_error_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -3,4 +3,10 @@
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
extern std::unordered_map<std::string, std::unordered_map<std::string, game::hks::lua_function>> libs;
|
||||
|
||||
void enable_error_hook();
|
||||
void disable_error_hook();
|
||||
|
||||
game::hks::lua_function find_function(const std::string& name);
|
||||
}
|
||||
|
@ -914,5 +914,69 @@ namespace game
|
||||
uint64_t streams[4];
|
||||
const char* name;
|
||||
};
|
||||
|
||||
namespace hks
|
||||
{
|
||||
struct InternString
|
||||
{
|
||||
unsigned __int64 m_flags;
|
||||
unsigned __int64 m_lengthbits;
|
||||
unsigned int m_hash;
|
||||
char m_data[30];
|
||||
};
|
||||
|
||||
union HksValue
|
||||
{
|
||||
void* cClosure;
|
||||
void* closure;
|
||||
void* userData;
|
||||
void* table;
|
||||
void* tstruct;
|
||||
InternString* str;
|
||||
void* thread;
|
||||
void* ptr;
|
||||
float number;
|
||||
unsigned int native;
|
||||
bool boolean;
|
||||
};
|
||||
|
||||
enum HksType
|
||||
{
|
||||
HKS_LUA_TNONE = 0xFFFFFFFF,
|
||||
HKS_LUA_TNIL = 0x0,
|
||||
HKS_LUA_TBOOLEAN = 0x1,
|
||||
HKS_LUA_TLIGHTUSERDATA = 0x2,
|
||||
HKS_LUA_TNUMBER = 0x3,
|
||||
HKS_LUA_TSTRING = 0x4,
|
||||
HKS_LUA_TTABLE = 0x5,
|
||||
HKS_LUA_TFUNCTION = 0x6,
|
||||
HKS_LUA_TUSERDATA = 0x7,
|
||||
HKS_LUA_TTHREAD = 0x8,
|
||||
HKS_LUA_TUI64 = 0xB,
|
||||
HKS_LUA_TSTRUCT = 0xC,
|
||||
};
|
||||
|
||||
struct HksObject
|
||||
{
|
||||
HksType t;
|
||||
HksValue v;
|
||||
};
|
||||
|
||||
struct lua_State
|
||||
{
|
||||
char __pad0[72];
|
||||
HksObject* top;
|
||||
HksObject* base;
|
||||
HksObject* alloc_top;
|
||||
HksObject* bottom;
|
||||
};
|
||||
|
||||
using lua_function = int(__fastcall*)(lua_State*);
|
||||
|
||||
struct luaL_Reg
|
||||
{
|
||||
const char* name;
|
||||
lua_function function;
|
||||
};
|
||||
}
|
||||
}
|
@ -147,4 +147,11 @@ namespace game
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xB617C00};
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0xBA9EE40};
|
||||
WEAK symbol<function_stack_t> scr_function_stack{0xBAA93C0};
|
||||
|
||||
namespace hks
|
||||
{
|
||||
WEAK symbol<lua_State*> lua_state{0x19D83E8};
|
||||
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x287410};
|
||||
WEAK symbol<const char*(lua_State* s, const HksObject* obj, unsigned int* len)> hks_obj_tolstring{0x287410};
|
||||
}
|
||||
}
|
145
src/client/game/ui_scripting/execution.cpp
Normal file
145
src/client/game/ui_scripting/execution.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include <std_include.hpp>
|
||||
#include "execution.hpp"
|
||||
|
||||
#include "component/ui_scripting.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool valid_state()
|
||||
{
|
||||
return *game::hks::lua_state != 0;
|
||||
}
|
||||
|
||||
void push_value(const value& value_)
|
||||
{
|
||||
if (!valid_state())
|
||||
{
|
||||
throw std::runtime_error("Invalid lua state");
|
||||
}
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
switch (value_.index())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
const auto value = std::get<bool>(value_);
|
||||
state->top->t = game::hks::HksType::HKS_LUA_TBOOLEAN;
|
||||
state->top->v.boolean = value;
|
||||
state->top++;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
const auto value = std::get<int>(value_);
|
||||
state->top->t = game::hks::HksType::HKS_LUA_TNUMBER;
|
||||
state->top->v.number = static_cast<float>(value);
|
||||
state->top++;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const auto value = std::get<float>(value_);
|
||||
state->top->t = game::hks::HksType::HKS_LUA_TNUMBER;
|
||||
state->top->v.number = value;
|
||||
state->top++;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
const auto str = std::get<std::string>(value_);
|
||||
game::hks::hksi_lua_pushlstring(state, str.data(), (unsigned int)str.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_integer(float number)
|
||||
{
|
||||
return static_cast<int>(number) == number;
|
||||
}
|
||||
|
||||
value get_return_value()
|
||||
{
|
||||
if (!valid_state())
|
||||
{
|
||||
throw std::runtime_error("Invalid lua state");
|
||||
}
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = &state->top[-1];
|
||||
|
||||
switch (top->t)
|
||||
{
|
||||
case game::hks::HksType::HKS_LUA_TBOOLEAN:
|
||||
{
|
||||
return {top->v.boolean};
|
||||
}
|
||||
break;
|
||||
case game::hks::HksType::HKS_LUA_TNUMBER:
|
||||
{
|
||||
const auto number = top->v.number;
|
||||
if (is_integer(number))
|
||||
{
|
||||
return {static_cast<int>(top->v.number)};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {top->v.number};
|
||||
}
|
||||
}
|
||||
break;
|
||||
case game::hks::HksType::HKS_LUA_TSTRING:
|
||||
{
|
||||
const auto value = top->v.str->m_data;
|
||||
return {std::string(value)};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value call(const std::string& name, const arguments& arguments)
|
||||
{
|
||||
if (!valid_state())
|
||||
{
|
||||
throw std::runtime_error("Invalid lua state");
|
||||
}
|
||||
|
||||
const auto function = ui_scripting::find_function(name);
|
||||
if (!function)
|
||||
{
|
||||
throw std::runtime_error("Function " + name + " not found");
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([]()
|
||||
{
|
||||
disable_error_hook();
|
||||
});
|
||||
|
||||
enable_error_hook();
|
||||
|
||||
for (const auto& value_ : arguments)
|
||||
{
|
||||
push_value(value_);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
function(*game::hks::lua_state);
|
||||
return get_return_value();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error("Error executing function " + name + ": " + e.what());
|
||||
}
|
||||
}
|
||||
}
|
9
src/client/game/ui_scripting/execution.hpp
Normal file
9
src/client/game/ui_scripting/execution.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
using value = std::variant<std::monostate, bool, int, float, std::string>;
|
||||
using arguments = std::vector<value>;
|
||||
value call(const std::string& name, const arguments& arguments);
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
#include "context.hpp"
|
||||
#include "error.hpp"
|
||||
#include "../../scripting/execution.hpp"
|
||||
#include "../execution.hpp"
|
||||
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
#include "../../../component/command.hpp"
|
||||
@ -953,27 +954,6 @@ namespace ui_scripting::lua
|
||||
::game::Dvar_SetCommand(hash, "", string_value.data());
|
||||
};
|
||||
|
||||
game_type["drawmaterial"] = [](const game&, float x, float y, float width, float height, float s0, float t0, float s1, float t1,
|
||||
const sol::lua_value& color_value, const std::string& material)
|
||||
{
|
||||
const auto color = color_value.as<std::vector<float>>();
|
||||
float _color[4] =
|
||||
{
|
||||
color[0],
|
||||
color[1],
|
||||
color[2],
|
||||
color[3],
|
||||
};
|
||||
|
||||
const auto _material = ::game::Material_RegisterHandle(material.data());
|
||||
::game::R_AddCmdDrawStretchPic(x, y, width, height, s0, t0, s1, t1, _color, _material);
|
||||
};
|
||||
|
||||
game_type["playsound"] = [](const game&, const std::string& sound)
|
||||
{
|
||||
::game::UI_PlayLocalSoundAlias(0, sound.data());
|
||||
};
|
||||
|
||||
game_type["getwindowsize"] = [](const game&, const sol::this_state s)
|
||||
{
|
||||
const auto size = ::game::ScrPlace_GetViewPlacement()->realViewportSize;
|
||||
@ -991,6 +971,49 @@ namespace ui_scripting::lua
|
||||
(::game::base_address + 0x71B970)(video.data(), 64, 0);
|
||||
};
|
||||
|
||||
game_type[sol::meta_function::index] = [](const game&, const std::string& name)
|
||||
{
|
||||
return [name](const game&, const sol::this_state s, sol::variadic_args va)
|
||||
{
|
||||
arguments arguments;
|
||||
|
||||
for (auto arg : va)
|
||||
{
|
||||
arguments.push_back(arg);
|
||||
}
|
||||
|
||||
const auto value = call(name, arguments);
|
||||
if (value.index() == 0)
|
||||
{
|
||||
return sol::lua_value{s, sol::lua_nil};
|
||||
}
|
||||
else
|
||||
{
|
||||
return sol::lua_value{s, value};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
game_type["call"] = [](const game&, const sol::this_state s, const std::string& name, sol::variadic_args va)
|
||||
{
|
||||
arguments arguments;
|
||||
|
||||
for (auto arg : va)
|
||||
{
|
||||
arguments.push_back(arg);
|
||||
}
|
||||
|
||||
const auto value = call(name, arguments);
|
||||
if (value.index() == 0)
|
||||
{
|
||||
return sol::lua_value{s, sol::lua_nil};
|
||||
}
|
||||
else
|
||||
{
|
||||
return sol::lua_value{s, value};
|
||||
}
|
||||
};
|
||||
|
||||
struct player
|
||||
{
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user