@@ -96,6 +96,19 @@ namespace RC
9696 return lua.get_bool (-1 );
9797 }
9898
99+ static auto get_function_name_without_prefix (const StringType& function_full_name) -> StringType
100+ {
101+ static constexpr StringViewType function_prefix{STR (" Function " )};
102+ if (auto prefix_pos = function_full_name.find (function_prefix); prefix_pos != function_full_name.npos )
103+ {
104+ return function_full_name.substr (prefix_pos + function_prefix.size ());
105+ }
106+ else
107+ {
108+ return function_full_name;
109+ }
110+ }
111+
99112 struct LuaUnrealScriptFunctionData
100113 {
101114 Unreal::CallbackId pre_callback_id;
@@ -110,6 +123,7 @@ namespace RC
110123 bool has_return_value{};
111124 // Will be non-nullptr if the UFunction has a return value
112125 Unreal::FProperty* return_property{};
126+ bool scheduled_for_removal{};
113127 };
114128 static std::vector<std::unique_ptr<LuaUnrealScriptFunctionData>> g_hooked_script_function_data{};
115129
@@ -215,6 +229,14 @@ namespace RC
215229 // The API will automatically call the original function
216230 // This function continues in 'lua_unreal_script_function_hook_post' which executes immediately after the original function gets called
217231
232+ if (lua_data.scheduled_for_removal )
233+ {
234+ auto native_hook_pre_id_it = LuaMod::m_generic_hook_id_to_native_hook_id.find (lua_data.pre_callback_id );
235+ auto function_name_no_prefix = get_function_name_without_prefix (lua_data.unreal_function ->GetFullName ());
236+ Output::send<LogLevel::Verbose>(STR (" Unregistering native pre-hook ({}) for {}\n " ), native_hook_pre_id_it->first , function_name_no_prefix);
237+ lua_data.unreal_function ->UnregisterHook (native_hook_pre_id_it->second );
238+ }
239+
218240 // No longer promising to be in the game thread
219241 set_is_in_game_thread (lua_data.lua , false );
220242 }
@@ -371,6 +393,25 @@ namespace RC
371393 process_return_value ();
372394 process_return_value ();
373395
396+ if (lua_data.scheduled_for_removal )
397+ {
398+ auto native_hook_post_id_it = LuaMod::m_generic_hook_id_to_native_hook_id.find (lua_data.post_callback_id );
399+ auto function_name_no_prefix = get_function_name_without_prefix (lua_data.unreal_function ->GetFullName ());
400+ Output::send<LogLevel::Verbose>(STR (" Unregistering native post-hook ({}) for {}\n " ), native_hook_post_id_it->first , function_name_no_prefix);
401+ lua_data.unreal_function ->UnregisterHook (native_hook_post_id_it->second );
402+
403+ auto mod = get_mod_ref (lua_data.lua );
404+ luaL_unref (lua_data.lua .get_lua_state (), LUA_REGISTRYINDEX, lua_data.lua_callback_ref );
405+ if (lua_data.lua_post_callback_ref != -1 )
406+ {
407+ luaL_unref (lua_data.lua .get_lua_state (), LUA_REGISTRYINDEX, lua_data.lua_post_callback_ref );
408+ }
409+ luaL_unref (mod->lua ().get_lua_state (), LUA_REGISTRYINDEX, lua_data.lua_thread_ref );
410+ std::erase_if (g_hooked_script_function_data, [&](const std::unique_ptr<LuaUnrealScriptFunctionData>& elem) {
411+ return elem.get () == &lua_data;
412+ });
413+ }
414+
374415 // No longer promising to be in the game thread
375416 set_is_in_game_thread (lua_data.lua , false );
376417 }
@@ -1137,19 +1178,6 @@ namespace RC
11371178 }
11381179 }
11391180
1140- static auto get_function_name_without_prefix (const StringType& function_full_name) -> StringType
1141- {
1142- static constexpr StringViewType function_prefix{STR (" Function " )};
1143- if (auto prefix_pos = function_full_name.find (function_prefix); prefix_pos != function_full_name.npos )
1144- {
1145- return function_full_name.substr (prefix_pos + function_prefix.size ());
1146- }
1147- else
1148- {
1149- return function_full_name;
1150- }
1151- }
1152-
11531181 auto static setup_lua_global_functions_internal (const LuaMadeSimple::Lua& lua, Mod::IsTrueMod is_true_mod) -> void
11541182 {
11551183 lua.register_function (" print" , LuaLibrary::global_print);
@@ -1717,38 +1745,25 @@ No overload found for function 'UnregisterHook'.
17171745 if (native_hook_pre_id_it != LuaMod::m_generic_hook_id_to_native_hook_id.end () &&
17181746 native_hook_post_id_it != LuaMod::m_generic_hook_id_to_native_hook_id.end ())
17191747 {
1720- Output::send<LogLevel::Verbose>(STR (" Unregistering native pre-hook ({}) for {}\n " ), native_hook_pre_id_it->first , function_name_no_prefix);
1721- unreal_function->UnregisterHook (static_cast <int32_t >(native_hook_pre_id_it->second ));
1722- Output::send<LogLevel::Verbose>(STR (" Unregistering native post-hook ({}) for {}\n " ), native_hook_post_id_it->first , function_name_no_prefix);
1723- unreal_function->UnregisterHook (static_cast <int32_t >(native_hook_post_id_it->second ));
1724-
1725- // LuaUnrealScriptFunctionData contains the hook's lua registry references, captured in RegisterHook in two different lua states.
1726- for (auto hook_iter = g_hooked_script_function_data.begin (); hook_iter != g_hooked_script_function_data.end (); hook_iter++)
1727- {
1728- RC::LuaUnrealScriptFunctionData* hook_data = (*hook_iter).get ();
1729- if (hook_data->post_callback_id == post_id && hook_data->pre_callback_id == pre_id)
1730- {
1731- auto mod = get_mod_ref (lua);
1732- luaL_unref (hook_data->lua .get_lua_state (), LUA_REGISTRYINDEX, hook_data->lua_callback_ref );
1733- if (hook_data->lua_post_callback_ref != -1 )
1734- {
1735- luaL_unref (hook_data->lua .get_lua_state (), LUA_REGISTRYINDEX, hook_data->lua_post_callback_ref );
1736- }
1737- luaL_unref (mod->lua ().get_lua_state (), LUA_REGISTRYINDEX, hook_data->lua_thread_ref );
1738- g_hooked_script_function_data.erase (hook_iter);
1739- break ;
1740- }
1741- }
1748+ const auto hook_data = std::ranges::find_if (g_hooked_script_function_data, [&](const std::unique_ptr<LuaUnrealScriptFunctionData>& elem) {
1749+ return elem->post_callback_id == post_id && elem->pre_callback_id == pre_id;
1750+ });
1751+ hook_data->get ()->scheduled_for_removal = true ;
17421752 }
17431753 else
17441754 {
17451755 if (auto data_ptr = LuaMod::find_function_hook_data (LuaMod::m_script_hook_callbacks, unreal_function); data_ptr)
17461756 {
17471757 Output::send<LogLevel::Verbose>(STR (" Unregistering script hook with id: {}, FunctionName: {}\n " ), post_id, function_name_no_prefix);
17481758 auto & registry_indexes = data_ptr->callback_data .registry_indexes ;
1749- std::erase_if (registry_indexes, [&](const auto & pair) -> bool {
1750- return post_id == pair.second .identifier ;
1751- });
1759+ for (auto & registry_index : registry_indexes)
1760+ {
1761+ if (post_id == registry_index.second .identifier )
1762+ {
1763+ registry_index.second .lua_index = -1 ;
1764+ break ;
1765+ }
1766+ }
17521767 }
17531768 }
17541769
@@ -4231,6 +4246,12 @@ No overload found for function 'FPackageName:IsValidLongPackageName'.
42314246 {
42324247 const auto & lua = *lua_ptr;
42334248
4249+ // -1 is a special value that signifies that the hook has been unregistered.
4250+ if (registry_index.lua_index == -1 )
4251+ {
4252+ continue ;
4253+ }
4254+
42344255 set_is_in_game_thread (lua, true );
42354256
42364257 lua.registry ().get_function_ref (registry_index.lua_index );
0 commit comments