@@ -317,6 +317,24 @@ genny::Enum* enum_from_name(genny::Namespace* g, const std::string& enum_name) {
317317 return new_ns->enum_ (namespaces.back ());
318318}
319319
320+ std::string get_method_prototype_context_name (sdk::REMethodDefinition& m) {
321+ auto method_name = m.get_name ();
322+ auto method_param_types = m.get_param_types ();
323+
324+ std::stringstream ss_context{};
325+ ss_context << method_name << " (" ;
326+
327+ for (auto i = 0 ; i < method_param_types.size (); i++) {
328+ if (i > 0 ) {
329+ ss_context << " , " ;
330+ }
331+ ss_context << method_param_types[i]->get_full_name ();
332+ }
333+
334+ ss_context << " )" ;
335+ return ss_context.str ();
336+ }
337+
320338std::shared_ptr<ObjectExplorer>& ObjectExplorer::get () {
321339 static auto instance = std::make_shared<ObjectExplorer>();
322340
@@ -863,6 +881,30 @@ void ObjectExplorer::display_hooks() {
863881 ImGui::EndCombo ();
864882 }
865883
884+ // Auto-unhook options
885+ ImGui::Checkbox (" Enable Auto Unhook" , &m_hooks_context.enable_auto_unhook );
886+
887+ if (m_hooks_context.enable_auto_unhook ) {
888+ if (ImGui::BeginCombo (" Auto Unhook When" , HooksContext::s_auto_unhook_method_names[(uint8_t )m_hooks_context.auto_unhook_method ])) {
889+ for (int i = 0 ; i < HooksContext::s_auto_unhook_method_names.size (); i++) {
890+ const bool is_selected = (m_hooks_context.auto_unhook_method == (HooksContext::AutoUnhookMethod)i);
891+
892+ if (ImGui::Selectable (HooksContext::s_auto_unhook_method_names[i], is_selected)) {
893+ m_hooks_context.auto_unhook_method = (HooksContext::AutoUnhookMethod)i;
894+ }
895+
896+ if (is_selected) {
897+ ImGui::SetItemDefaultFocus ();
898+ }
899+ }
900+
901+ ImGui::EndCombo ();
902+ }
903+
904+ // Always make it editable regardless of the auto unhook method, this is intentional
905+ ImGui::InputScalar (" Max Call Count" , ImGuiDataType_U32, &m_hooks_context.auto_unhook_call_count_threshold );
906+ }
907+
866908 ImGui::TreePop ();
867909 }
868910
@@ -4269,7 +4311,7 @@ void ObjectExplorer::hook_method(sdk::REMethodDefinition* method, std::optional<
42694311 if (name) {
42704312 hooked.name = method->get_declaring_type ()->get_full_name () + " ." + *name;
42714313 } else {
4272- hooked.name = method->get_declaring_type ()->get_full_name () + " ." + method-> get_name ( );
4314+ hooked.name = method->get_declaring_type ()->get_full_name () + " ." + get_method_prototype_context_name (*method );
42734315 }
42744316
42754317 using namespace asmjit ;
@@ -4342,6 +4384,23 @@ void ObjectExplorer::hook_all_methods(sdk::RETypeDefinition* t) {
43424384 }
43434385}
43444386
4387+ void ObjectExplorer::unhook_all_methods (sdk::RETypeDefinition* t) {
4388+ if (t == nullptr ) {
4389+ return ;
4390+ }
4391+
4392+ const auto methods = t->get_methods ();
4393+
4394+ for (auto & m : methods) {
4395+ auto it = std::find_if (m_hooked_methods.begin (), m_hooked_methods.end (), [&m](auto & hook) { return hook.method == &m; });
4396+
4397+ if (it != m_hooked_methods.end ()) {
4398+ g_hookman.remove (it->method , it->hook_id );
4399+ m_hooked_methods.erase (it);
4400+ }
4401+ }
4402+ }
4403+
43454404void ObjectExplorer::method_context_menu (sdk::REMethodDefinition* method, std::optional<std::string> name, ::REManagedObject* obj) {
43464405 auto additional_ctx = [&]() {
43474406 auto it = std::find_if (m_hooked_methods.begin (), m_hooked_methods.end (), [method](auto & hook) { return hook.method == method; });
@@ -4377,6 +4436,18 @@ void ObjectExplorer::method_context_menu(sdk::REMethodDefinition* method, std::o
43774436 }
43784437 }
43794438
4439+ if (ImGui::Selectable (" Unhook All Methods" )) {
4440+ const auto declaring_type = method->get_declaring_type ();
4441+
4442+ if (declaring_type != nullptr ) {
4443+ std::scoped_lock _{m_job_mutex};
4444+
4445+ m_frame_jobs.push_back ([this , declaring_type]() {
4446+ unhook_all_methods (declaring_type);
4447+ });
4448+ }
4449+ }
4450+
43804451 // Allow us to call really simple methods with no params
43814452 if (method->get_param_types ().size () == 0 ) {
43824453 if (obj != nullptr || method->is_static ()) {
@@ -5385,6 +5456,20 @@ void ObjectExplorer::post_hooked_method_internal(uintptr_t& ret_val, sdk::REType
53855456 const auto delta = now - hooked_method.stats .last_call_times [tid];
53865457 hooked_method.stats .last_call_delta = delta;
53875458 hooked_method.stats .total_call_time += delta;
5459+
5460+ // Auto-unhook check
5461+ if (m_hooks_context.enable_auto_unhook && m_hooks_context.auto_unhook_method == HooksContext::AutoUnhookMethod::EXCEED_CALL_COUNT) {
5462+ if (hooked_method.stats .call_count >= m_hooks_context.auto_unhook_call_count_threshold ) {
5463+ std::scoped_lock job_lock{m_job_mutex};
5464+ m_frame_jobs.push_back ([this , method]() {
5465+ auto it2 = std::find_if (m_hooked_methods.begin (), m_hooked_methods.end (), [method](auto & a) { return a.method == method; });
5466+ if (it2 != m_hooked_methods.end ()) {
5467+ g_hookman.remove (it2->method , it2->hook_id );
5468+ m_hooked_methods.erase (it2);
5469+ }
5470+ });
5471+ }
5472+ }
53885473}
53895474
53905475void ObjectExplorer::post_hooked_method (uintptr_t & ret_val, sdk::RETypeDefinition*& ret_ty, uintptr_t ret_addr, sdk::REMethodDefinition* method) {
0 commit comments