Skip to content

Commit

Permalink
Lua: Add thread API for temporary hook storage and thread IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Mar 30, 2024
1 parent 03bbc49 commit 8e9375c
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/mods/ScriptRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ void debug(const char* str) {
}
}

namespace api::thread {
size_t get_hash() {
const auto id = std::this_thread::get_id();
return std::hash<std::thread::id>{}(id);
}

uint32_t get_id() {
return std::this_thread::get_id()._Get_underlying_id();
}

sol::object get_hook_storage(sol::this_state s, size_t hash) {
auto sol_state = sol::state_view{s};
auto state = sol_state.registry()["state"].get<ScriptState*>();
auto result = state->get_hook_storage(get_hash());

if (!result.has_value()) {
return sol::make_object(s, sol::lua_nil);
}

return sol::make_object(s, result.value());
}
}

ScriptState::ScriptState(const ScriptState::GarbageCollectionData& gc_data,bool is_main_state) {
std::scoped_lock _{ m_execution_mutex };
m_is_main_state = is_main_state;
Expand Down Expand Up @@ -88,6 +111,11 @@ ScriptState::ScriptState(const ScriptState::GarbageCollectionData& gc_data,bool
re["on_config_save"] = [this](sol::function fn) { m_on_config_save_fns.emplace_back(fn); };
m_lua["re"] = re;

auto thread = m_lua.create_table();
thread["get_hash"] = api::thread::get_hash;
thread["get_id"] = api::thread::get_id;
thread["get_hook_storage"] = api::thread::get_hook_storage;
m_lua["thread"] = thread;

auto log = m_lua.create_table();
log["info"] = api::log::info;
Expand Down Expand Up @@ -545,6 +573,8 @@ void ScriptState::install_hooks() {
}

try {
state->push_hook_storage(std::hash<std::thread::id>{}(std::this_thread::get_id()));

if (pre_cb.is<sol::nil_t>()) {
return result;
}
Expand Down Expand Up @@ -591,6 +621,7 @@ void ScriptState::install_hooks() {

try {
if (post_cb.is<sol::nil_t>()) {
state->pop_hook_storage(std::hash<std::thread::id>{}(std::this_thread::get_id()));
return;
}

Expand All @@ -601,6 +632,7 @@ void ScriptState::install_hooks() {
}

ret_val = (uintptr_t)script_result.get<void*>();
state->pop_hook_storage(std::hash<std::thread::id>{}(std::this_thread::get_id()));
} catch (const std::exception& e) {
ScriptRunner::get()->spew_error(e.what());
} catch (...) {
Expand Down
40 changes: 40 additions & 0 deletions src/mods/ScriptRunner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,44 @@ class ScriptState {

void gc_data_changed(GarbageCollectionData data);

/*sol::table get_thread_storage(size_t hash) {
auto it = m_thread_storage.find(hash);
if (it == m_thread_storage.end()) {
it = m_thread_storage.emplace(hash, m_lua.create_table()).first;
}
return it->second;
}*/

void push_hook_storage(size_t thread_hash) {
auto it = m_hook_storage.find(thread_hash);
if (it == m_hook_storage.end()) {
it = m_hook_storage.emplace(thread_hash, std::stack<sol::table>{}).first;
}

it->second.push(m_lua.create_table());
}

void pop_hook_storage(size_t thread_hash) {
auto it = m_hook_storage.find(thread_hash);
if (it != m_hook_storage.end()) {
if (!it->second.empty()) {
it->second.pop();
}
}
}

std::optional<sol::table> get_hook_storage(size_t thread_hash) {
auto it = m_hook_storage.find(thread_hash);
if (it != m_hook_storage.end()) {
if (!it->second.empty()) {
return it->second.top();
}
}

return std::nullopt;
}

private:
sol::state m_lua{};

Expand Down Expand Up @@ -182,6 +220,8 @@ class ScriptState {

std::deque<HookDef> m_hooks_to_add{};
std::unordered_map<sdk::REMethodDefinition*, std::vector<HookManager::HookId>> m_hooks{};

std::unordered_map<size_t, std::stack<sol::table>> m_hook_storage{};
};

class ScriptRunner : public Mod {
Expand Down

1 comment on commit 8e9375c

@praydog
Copy link
Owner Author

@praydog praydog commented on 8e9375c Mar 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am recommending use of this in Lua, rather than storing globals now. Lua API no longer has a guarantee that the hook will be locked between the pre and the post hook.

Example:

local pawn_t = sdk.find_type_definition("app.Pawn")

sdk.hook(
    pawn_t:get_method("updateMove"),
    function(args)
        local storage = thread.get_hook_storage()
        storage["this"] = sdk.to_managed_object(args[2])
    end,
    function(retval)
        local this = thread.get_hook_storage()["this"]
        print("this: " .. tostring(this:get_type_definition():get_full_name()))
        return retval
    end
)

This storage is ephemeral, and gets destroyed after the post function leaves its scope of execution.

Please sign in to comment.