- 1,531
- 501
Вот упрощённый вариант частей моей dll-ки. Я ставлю хук на функцию игры и из lua устанавливаю калбек, который будет вызываться из оригинального C++ калбека. Всё отлично работает, но есть проблема с обработкой ошибок.
Если внутри lua калбека происходит какая-то ошибка (у меня искуственно при помощи
Я могу заменить sol::unsafe_function на sol::protected_function, тогда sol обернёт вызов lua функции в pcall, который отловит ошибку в lua калбеке и трамплин выполнится. Но, в этом случаи lua скрипт не крашнется. Ошибка была отловлена и "проглочена" при помощи pcall, в C++ мы её дальше никак не обрабатываем. А это мне не подходит, я хочу получать краш lua скрипта при ошибках в lua калбеке.
Если добавить проброс пойманной ошибки через throw это ни к чему хорошему не приведёт. Оригинальный C++ калбек хука handleHookedFunc выполняется непосредственно в том же потоке что и захуканная функция, т.е. в потоке игры, а это значит что исключение пробрасывается внутрь игры, это если не брать в расчёт что трамплин, который идёт позже также не вызывается. Игра также умирает.
Теперь смотрим на moonloader. Там через addEventHandler можно установить калбека на всякие события, например "onReceivePacket" (событие отправки пакета данный на сервер). Эти события по сути также хуки каких-то функций/методов в игре. И внутри этих lua калбеков ошибки обрабатываются так как я хочу. Они крашат lua скрипт, при этом это никак не мешает работе игры, несмотря на то что вызов lua калбека идёт из хука.
Как мне реализовать такую же обработку ошибок как в moonloader? Чтобы ошибки внутри lua калбека крашили скрипт, а не игнорировались, но не ломали хук из которого они вызываются?
C++:
// установка lua калбека для вызова из хука
void set_callback(sol::this_state ts, sol::unsafe_function func)
{
gHandler = func;
}
// вызывается при подключении библиотеки из lua через require
sol::table open(sol::this_state ts)
{
sol::state_view lua(ts);
installHook();
module.set_function("set_callback", &set_cb);
return module;
}
extern "C" __declspec(dllexport) int luaopen_lib(lua_State* L) {
return (sol::c_call<decltype(&open), &open>)(L);
}
sol::unsafe_function gHandler; // тут будет хранится функция lua калбека
kthook::kthook_simple<bool(__thiscall*)(void*)> mHookedFunc; // объект хука
// функция установки C++ хука
void installHook() {
mHookedFunc.set_dest(0x123456); // адрес функции в игре на которую нужно поставить хук
mHookedFunc.set_cb(handleHookedFunc); // калбек, который будет срабатывать при вызове функции
mHookedFunc.install() // устанавливает хук
}
// калбек захуканной функции
bool handleHookedFunc(const kthook::kthook_simple<bool(__thiscall*)(void*)>& hook, void* ptr)
{
gHandler(); // вызов lua калбека. Если он пройдёт с ошибкой управление текущему потоку не вернётся и код ниже не выполнится
return hook.call_trampoline(ptr); // трамплин на оригинальную функцию
}
Lua:
local lib = require 'lib'
lib.set_callback(function()
error("LUA ERROR") -- условно какая-то ошибка.
end)
error("CRASH")
) то lua скрипт закономерно должен крашнуться. Где-то внутри вызова gHandler()
выполнится lua_error (через sol) и ошибка дойдёт до lua. Но, проблема в том что в этом случаи управление потоку не будет возвращено и весь код после вызова неудачно выполненной lua функции gHandler()
не отработает. Соответственно в представленном варианте return hook.call_trampoline(ptr);
не выполнится, а это нарушает хука и игра разумеется умирает.Я могу заменить sol::unsafe_function на sol::protected_function, тогда sol обернёт вызов lua функции в pcall, который отловит ошибку в lua калбеке и трамплин выполнится. Но, в этом случаи lua скрипт не крашнется. Ошибка была отловлена и "проглочена" при помощи pcall, в C++ мы её дальше никак не обрабатываем. А это мне не подходит, я хочу получать краш lua скрипта при ошибках в lua калбеке.
Если добавить проброс пойманной ошибки через throw это ни к чему хорошему не приведёт. Оригинальный C++ калбек хука handleHookedFunc выполняется непосредственно в том же потоке что и захуканная функция, т.е. в потоке игры, а это значит что исключение пробрасывается внутрь игры, это если не брать в расчёт что трамплин, который идёт позже также не вызывается. Игра также умирает.
C++:
bool handleHookedFunc(const kthook::kthook_simple<bool(__thiscall*)(void*)>& hook, void* ptr)
{
sol::protected_function_result result = gHandler(); // вызов lua калбека. Если он пройдёт с ошибкой управление текущему потоку не вернётся и код ниже не выполнится
if (!result.valid())
{
sol::error err = result;
throw err;
}
return hook.call_trampoline(ptr); // трамплин на оригинальную функцию
}
Теперь смотрим на moonloader. Там через addEventHandler можно установить калбека на всякие события, например "onReceivePacket" (событие отправки пакета данный на сервер). Эти события по сути также хуки каких-то функций/методов в игре. И внутри этих lua калбеков ошибки обрабатываются так как я хочу. Они крашат lua скрипт, при этом это никак не мешает работе игры, несмотря на то что вызов lua калбека идёт из хука.
Lua:
addEventHandler('onReceivePacket', function(id, bs)
error('LUA ERROR')
end)
Как мне реализовать такую же обработку ошибок как в moonloader? Чтобы ошибки внутри lua калбека крашили скрипт, а не игнорировались, но не ломали хук из которого они вызываются?