From 37a2abb654699799e5386816b6b336abf8887d8e Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 14 Dec 2024 13:19:52 +0100 Subject: [PATCH 1/6] feat(Lua): (Un)RegisterHook now provides the function name in error messages --- UE4SS/src/Mod/LuaMod.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index afaa46053..85c2881c8 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -1366,7 +1366,9 @@ No overload found for function 'UnregisterHook'. Unreal::UFunction* unreal_function = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, function_name_no_prefix); if (!unreal_function) { - lua.throw_error("Tried to unregister a hook with Lua function 'UnregisterHook' but no UFunction with the specified name was found."); + lua.throw_error(std::format("Tried to unregister a hook with Lua function 'UnregisterHook' but no UFunction with the specified name " + "was found.\n>FunctionName: {}", + to_string(function_name_no_prefix))); } if (!lua.is_integer()) @@ -1383,12 +1385,16 @@ No overload found for function 'UnregisterHook'. if (pre_id > std::numeric_limits::max()) { - lua.throw_error("Tried to unregister a hook with Lua function 'UnregisterHook' but the PreCallbackId supplied was too large (>int32)"); + lua.throw_error(std::format("Tried to unregister a hook with Lua function 'UnregisterHook' but the PreCallbackId supplied was too " + "large (>int32)\n>FunctionName: {}", + to_string(function_name_no_prefix))); } if (post_id > std::numeric_limits::max()) { - lua.throw_error("Tried to unregister a hook with Lua function 'UnregisterHook' but the PostCallbackId supplied was too large (>int32)"); + lua.throw_error(std::format("Tried to unregister a hook with Lua function 'UnregisterHook' but the PostCallbackId supplied was too " + "large (>int32)\n>FunctionName: {}", + to_string(function_name_no_prefix))); } // Hooks on native UFunctions will have both of these IDs. @@ -1397,9 +1403,9 @@ No overload found for function 'UnregisterHook'. if (native_hook_pre_id_it != LuaMod::m_generic_hook_id_to_native_hook_id.end() && native_hook_post_id_it != LuaMod::m_generic_hook_id_to_native_hook_id.end()) { - Output::send(STR("Unregistering native hook with pre-id: {}\n"), native_hook_pre_id_it->first); + Output::send(STR("Unregistering native pre-hook ({}) for {}\n"), native_hook_pre_id_it->first, function_name_no_prefix); unreal_function->UnregisterHook(static_cast(native_hook_pre_id_it->second)); - Output::send(STR("Unregistering native hook with post-id: {}\n"), native_hook_post_id_it->first); + Output::send(STR("Unregistering native post-hook ({}) for {}\n"), native_hook_post_id_it->first, function_name_no_prefix); unreal_function->UnregisterHook(static_cast(native_hook_post_id_it->second)); // LuaUnrealScriptFunctionData contains the hook's lua registry references, captured in RegisterHook in two different lua states. @@ -1425,7 +1431,7 @@ No overload found for function 'UnregisterHook'. if (auto callback_data_it = LuaMod::m_script_hook_callbacks.find(unreal_function->GetFullName()); callback_data_it != LuaMod::m_script_hook_callbacks.end()) { - Output::send(STR("Unregistering script hook with id: {}\n"), post_id); + Output::send(STR("Unregistering script hook with id: {}, FunctionName: {}\n"), post_id, function_name_no_prefix); auto& registry_indexes = callback_data_it->second.registry_indexes; std::erase_if(registry_indexes, [&](const auto& pair) -> bool { return post_id == pair.second.identifier; @@ -3020,7 +3026,9 @@ No overload found for function 'RegisterHook'. Unreal::UFunction* unreal_function = Unreal::UObjectGlobals::StaticFindObject(nullptr, nullptr, function_name_no_prefix); if (!unreal_function) { - lua.throw_error("Tried to register a hook with Lua function 'RegisterHook' but no UFunction with the specified name was found."); + lua.throw_error(std::format( + "Tried to register a hook with Lua function 'RegisterHook' but no UFunction with the specified name was found.\nFunction Name: {}", + to_string(function_name_no_prefix))); } int32_t generic_pre_id{}; @@ -3062,6 +3070,7 @@ No overload found for function 'RegisterHook'. else { std::string error_message{"Was unable to register a hook with Lua function 'RegisterHook', information:\n"}; + error_message.append(fmt::format("FunctionName: {}\n", to_string(function_name_no_prefix))); error_message.append(fmt::format("UFunction::Func: {}\n", std::bit_cast(func_ptr))); error_message.append(fmt::format("ProcessInternal: {}\n", Unreal::UObject::ProcessInternalInternal.get_function_address())); error_message.append( From 4588590f1d9d29d4828ea8084f5929f610665c5c Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 14 Dec 2024 13:46:29 +0100 Subject: [PATCH 2/6] fix(Lua): Errors/warnings now have color across multiple lines in the GUI console --- UE4SS/src/GUI/ConsoleOutputDevice.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UE4SS/src/GUI/ConsoleOutputDevice.cpp b/UE4SS/src/GUI/ConsoleOutputDevice.cpp index 7e0ac8e67..2552d0573 100644 --- a/UE4SS/src/GUI/ConsoleOutputDevice.cpp +++ b/UE4SS/src/GUI/ConsoleOutputDevice.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -27,7 +28,12 @@ namespace RC::Output fmt_copy.pop_back(); } auto color = static_cast(optional_arg); - UE4SSProgram::get_program().get_debugging_ui().get_console().add_line(m_formatter(fmt_copy), color); + auto formatted_message = m_formatter(fmt_copy); + std::wstringstream stream{formatted_message}; + for (File::StringType line; std::getline(stream, line);) + { + UE4SSProgram::get_program().get_debugging_ui().get_console().add_line(line, color); + } #endif } } // namespace RC::Output From 259391729323935b2c835d791e89995a3e470840 Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 14 Dec 2024 13:53:14 +0100 Subject: [PATCH 3/6] feat(Lua): An uninstallable mod now appears as a warning in the console --- UE4SS/src/UE4SSProgram.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UE4SS/src/UE4SSProgram.cpp b/UE4SS/src/UE4SSProgram.cpp index f654b8fe2..c165ae402 100644 --- a/UE4SS/src/UE4SSProgram.cpp +++ b/UE4SS/src/UE4SSProgram.cpp @@ -1050,19 +1050,19 @@ namespace RC if (mod_name_is_taken) { mod->set_installable(false); - Output::send(STR("Mod name '{}' is already in use.\n"), mod->get_name()); + Output::send(STR("Mod name '{}' is already in use.\n"), mod->get_name()); continue; } if (mod->is_installed()) { - Output::send(STR("Tried to install a mod that was already installed, Mod: '{}'\n"), mod->get_name()); + Output::send(STR("Tried to install a mod that was already installed, Mod: '{}'\n"), mod->get_name()); continue; } if (!mod->is_installable()) { - Output::send(STR("Was unable to install mod '{}' for unknown reasons. Mod is not installable.\n"), mod->get_name()); + Output::send(STR("Was unable to install mod '{}' for unknown reasons. Mod is not installable.\n"), mod->get_name()); continue; } From 1673f5feb6036b647ae58e4a5d750966d0659f25 Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 14 Dec 2024 14:57:15 +0100 Subject: [PATCH 4/6] feat(Lua): Lua errors on keybind now appear as errors in the console --- UE4SS/src/Mod/LuaMod.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index 85c2881c8..ef56999d6 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -1142,7 +1142,7 @@ No overload found for function 'RegisterKeyBindAsync'. } catch (std::runtime_error& e) { - Output::send(STR("{}\n"), ensure_str(lua.handle_error(e.what()))); + Output::send(STR("{}\n"), ensure_str(lua.handle_error(e.what()))); } }; @@ -1261,7 +1261,7 @@ No overload found for function 'RegisterKeyBind'. } catch (std::runtime_error& e) { - Output::send(STR("{}\n"), ensure_str(lua.handle_error(e.what()))); + Output::send(STR("{}\n"), ensure_str(lua.handle_error(e.what()))); } }; From 9c73f15a0cb297605d010c557d91e936d3de2e70 Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 14 Dec 2024 17:12:48 +0100 Subject: [PATCH 5/6] feat(Lua): Property pushers now output operational information on error --- UE4SS/include/LuaType/LuaUObject.hpp | 62 ++++++++++++++- UE4SS/src/LuaType/LuaUObject.cpp | 115 ++++++++++++++------------- 2 files changed, 120 insertions(+), 57 deletions(-) diff --git a/UE4SS/include/LuaType/LuaUObject.hpp b/UE4SS/include/LuaType/LuaUObject.hpp index f7b49a012..c78704f57 100644 --- a/UE4SS/include/LuaType/LuaUObject.hpp +++ b/UE4SS/include/LuaType/LuaUObject.hpp @@ -234,6 +234,62 @@ namespace RC::LuaType // Whether to create a new Lua item (example: table) or use an existing one on the top of the stack bool create_new_if_get_non_trivial_local{true}; + + auto get_operation() const -> const char* + { + switch (operation) + { + case Operation::Get: + return "Get"; + case Operation::GetNonTrivialLocal: + return "GetNonTrivialLocal"; + case Operation::Set: + return "Set"; + case Operation::GetParam: + return "GetParam"; + } + return "UnknownOperation"; + } + + auto throw_error_internal_append_args(std::string&) const -> void + { + } + template + auto throw_error_internal_append_args(std::string& error_message, K1&& key, V1&& value) const -> void + { + error_message.append(fmt::format(" {}: {}\n", key, value)); + } + template + auto throw_error_internal_append_args(std::string& error_message, K1&& key, V1&& value, Remaining&&... remaining) const -> void + { + error_message.append(fmt::format(" {}: {}\n", key, value)); + throw_error_internal_append_args(error_message, remaining...); + } + // Helper to throw nicely formatted error messages in property pushers. + // You can supply extra data by passing two extra args per data. + // The first arg is the name of the data, it must be a string. + // The second arg is anything that fmt::format can format. + // Example: throw_error("push_objectproperty", "Value must be UObject or nil", "extra_data", 1234) + // Output: + // [push_objectproperty] Error: + // Value must be UObject or nil + // Property: ObjectProperty Children./Script/Engine.Actor:Children + // extra_data: 1234 + template + auto throw_error(std::string_view handler_name, std::string_view format, Args&&... args) const -> void + { + std::string error_message{}; + error_message.append(fmt::format("[{}] Error:\n", handler_name)); + error_message.append(fmt::format(" {}\n", format)); + error_message.append(fmt::format(" Operation: {}\n", get_operation())); + error_message.append(fmt::format(" Base: {}\n", static_cast(base))); + error_message.append(fmt::format(" Data: {}\n", data)); + error_message.append(fmt::format(" Property ({}): {}\n", static_cast(property), property ? to_string(property->GetFullName()) : "null")); + error_message.append(fmt::format(" StoredAtIndex: {}\n", stored_at_index)); + error_message.append(fmt::format(" CreateNewIfGetNonTrivialLocal: {}\n", create_new_if_get_non_trivial_local)); + throw_error_internal_append_args(error_message, args...); + lua.throw_error(error_message); + } }; struct RC_UE4SS_API FunctionPusherParams @@ -820,7 +876,7 @@ No overload found for function 'UObject.ProcessConsoleExec'. IntegerType* integer_ptr = static_cast(params.data); if (!integer_ptr) { - params.lua.throw_error("[push_integer] data pointer is nullptr"); + params.throw_error("push_integer", "data pointer is nullptr"); } switch (params.operation) @@ -836,10 +892,10 @@ No overload found for function 'UObject.ProcessConsoleExec'. RemoteUnrealParam::construct(params.lua, integer_ptr, params.base, params.property); return; default: - params.lua.throw_error("[push_integer] Unhandled Operation"); + params.throw_error("push_integer", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_integer] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_integer", "Operation not supported"); } } // namespace RC::LuaType diff --git a/UE4SS/src/LuaType/LuaUObject.cpp b/UE4SS/src/LuaType/LuaUObject.cpp index adcb065b4..b90f47f64 100644 --- a/UE4SS/src/LuaType/LuaUObject.cpp +++ b/UE4SS/src/LuaType/LuaUObject.cpp @@ -404,7 +404,7 @@ namespace RC::LuaType } else { - params.lua.throw_error("[push_objectproperty] Value must be UObject or nil"); + params.throw_error("push_objectproperty", "Value must be UObject or nil"); } break; } @@ -412,7 +412,7 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); break; default: - params.lua.throw_error("[push_objectproperty] Unhandled Operation"); + params.throw_error("push_objectproperty", "Unhandled Operation"); break; } } @@ -439,7 +439,7 @@ namespace RC::LuaType } else { - params.lua.throw_error("[push_classproperty] Value must be UClass or nil"); + params.throw_error("push_classproperty", "Value must be UClass or nil"); } break; } @@ -447,7 +447,7 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); break; default: - params.lua.throw_error("[push_classproperty] Unhandled Operation"); + params.throw_error("push_classproperty", "Unhandled Operation"); break; } } @@ -563,9 +563,12 @@ namespace RC::LuaType else { std::string field_type_name = to_string(field_type.ToString()); - lua.throw_error(fmt::format("Tried getting without a registered handler. 'StructProperty'.'{}' not supported. Field: '{}'", - field_type_name, - field_name)); + params.throw_error("push_structproperty", + "Tried getting without a registered handler.", + "StructProperty field type not supported", + field_type_name, + "Field", + field_name); } }; @@ -623,9 +626,12 @@ namespace RC::LuaType else { std::string field_type_name = to_string(field_type_fname.ToString()); - params.lua.throw_error(fmt::format("Tried pushing (Operation::Set) StructProperty without a registered handler for field '{} {}'.", - field_type_name, - field_name)); + params.throw_error("push_structproperty", + "Tried pushing StructProperty without a registered handler for field.", + "Field type", + field_type_name, + "Field", + field_name); } }; @@ -638,7 +644,7 @@ namespace RC::LuaType if (params.lua.is_userdata()) { // StructData as userdata - params.lua.throw_error("[push_structproperty::lua_to_memory] StructData as userdata is not yet implemented but there's userdata on the stack"); + params.throw_error("push_structproperty::lua_to_memory", "StructData as userdata is not yet implemented but there's userdata on the stack"); } else if (params.lua.is_table()) { @@ -651,7 +657,7 @@ namespace RC::LuaType } else { - params.lua.throw_error("[push_structproperty::lua_to_memory] Parameter must be of type 'StructProperty' or table"); + params.throw_error("push_structproperty::lua_to_memory", "Parameter must be of type 'StructProperty' or table"); } }; @@ -672,11 +678,11 @@ namespace RC::LuaType LocalUnrealParam::construct(params.lua, property_value, params.base, params.property); return; default: - params.lua.throw_error("[push_structproperty] Unhandled Operation"); + params.throw_error("push_structproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_structproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_structproperty", "Operation not supported"); } auto push_arrayproperty(const PusherParams& params) -> void @@ -725,9 +731,10 @@ namespace RC::LuaType else { std::string property_type_name = to_string(property_type_fname.ToString()); - lua.throw_error( - fmt::format("Tried interacting with an array but the inner property has no registered handler. Property type '{}' not supported.", - property_type_name)); + params.throw_error("push_arrayproperty", + "Tried interacting with an array but the inner property has no registered handler.", + "Inner property type", + property_type_name); } }; @@ -740,7 +747,7 @@ namespace RC::LuaType if (!StaticState::m_property_value_pushers.contains(name_comparison_index)) { std::string inner_type_name = to_string(inner_type_fname.ToString()); - params.lua.throw_error(fmt::format("Tried pushing (Operation::Set) ArrayProperty with unsupported inner type of '{}'", inner_type_name)); + params.throw_error("push_arrayproperty", "Tried pushing ArrayProperty with unsupported inner type", "Inner property type", inner_type_name); } size_t array_element_size = inner->GetElementSize(); @@ -804,7 +811,7 @@ namespace RC::LuaType if (params.lua.is_userdata()) { // TArray as userdata - params.lua.throw_error("[push_arrayproperty::lua_to_memory] StructData as userdata is not yet implemented but there's userdata on the stack"); + params.throw_error("push_arrayproperty::lua_to_memory", "StructData as userdata is not yet implemented but there's userdata on the stack"); } else if (params.lua.is_table()) { @@ -817,7 +824,7 @@ namespace RC::LuaType } else { - params.lua.throw_error("[push_arrayproperty::lua_to_memory] Parameter must be of type 'StructProperty' or table"); + params.throw_error("push_arrayproperty::lua_to_memory", "Parameter must be of type 'StructProperty' or table"); } }; @@ -837,11 +844,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); return; default: - params.lua.throw_error("[push_arrayproperty] Unhandled Operation"); + params.throw_error("push_arrayproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_arrayproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_arrayproperty", "Operation not supported"); } auto push_functionproperty(const FunctionPusherParams& params) -> void @@ -854,7 +861,7 @@ namespace RC::LuaType float* float_ptr = static_cast(params.data); if (!float_ptr) { - params.lua.throw_error("[push_floatproperty] data pointer is nullptr"); + params.throw_error("push_floatproperty", "data pointer is nullptr"); } switch (params.operation) @@ -870,11 +877,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, float_ptr, params.base, params.property); return; default: - params.lua.throw_error("[push_floatproperty] Unhandled Operation"); + params.throw_error("push_floatproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_floatproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_floatproperty", "Operation not supported"); } auto push_doubleproperty(const PusherParams& params) -> void @@ -882,7 +889,7 @@ namespace RC::LuaType double* double_ptr = static_cast(params.data); if (!double_ptr) { - params.lua.throw_error("[push_doubleproperty] data pointer is nullptr"); + params.throw_error("push_doubleproperty", "data pointer is nullptr"); } switch (params.operation) @@ -898,11 +905,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, double_ptr, params.base, params.property); return; default: - params.lua.throw_error("[push_doubleproperty] Unhandled Operation"); + params.throw_error("push_doubleproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_doubleproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_doubleproperty", "Operation not supported"); } auto push_boolproperty(const PusherParams& params) -> void @@ -910,7 +917,7 @@ namespace RC::LuaType uint8_t* bitfield_ptr = static_cast(params.data); if (!bitfield_ptr) { - params.lua.throw_error("[push_boolproperty] data pointer is nullptr"); + params.throw_error("push_boolproperty", "data pointer is nullptr"); } Unreal::FBoolProperty* bp = static_cast(params.property); @@ -936,11 +943,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, bitfield_ptr, params.base, params.property); return; default: - params.lua.throw_error("[push_boolproperty] Unhandled Operation"); + params.throw_error("push_boolproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_boolproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_boolproperty", "Operation not supported"); } auto push_enumproperty(const PusherParams& params) -> void @@ -948,7 +955,7 @@ namespace RC::LuaType Unreal::UEnum* enum_ptr = static_cast(params.property)->GetEnum(); if (!enum_ptr) { - params.lua.throw_error("[push_enumproperty] data pointer is nullptr"); + params.throw_error("push_enumproperty", "data pointer is nullptr"); } switch (params.operation) @@ -986,18 +993,18 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); return; default: - params.lua.throw_error("[push_enumproperty] Unhandled Operation"); + params.throw_error("push_enumproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_enumproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_enumproperty", "Operation not supported"); } auto push_weakobjectproperty(const PusherParams& params) -> void { if (!params.data) { - params.lua.throw_error("[push_weakobjectproperty] data pointer is nullptr"); + params.throw_error("push_weakobjectproperty", "data pointer is nullptr"); } switch (params.operation) @@ -1019,14 +1026,14 @@ namespace RC::LuaType Output::send(STR("[push_weakobjectproperty] Operation::Set is not supported\n")); return; case Operation::GetParam: - params.lua.throw_error("[push_weakobjectproperty] Operation::GetParam is not supported"); + params.throw_error("push_weakobjectproperty", "Operation::GetParam is not supported"); return; default: - params.lua.throw_error("[push_weakobjectproperty] Unhandled Operation"); + params.throw_error("push_weakobjectproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_weakobjectproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_weakobjectproperty", "Operation not supported"); } auto push_nameproperty(const PusherParams& params) -> void @@ -1034,7 +1041,7 @@ namespace RC::LuaType Unreal::FName* name = static_cast(params.data); if (!name) { - params.lua.throw_error("[push_nameproperty] data pointer is nullptr"); + params.throw_error("push_nameproperty", "data pointer is nullptr"); } switch (params.operation) @@ -1052,11 +1059,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); return; default: - params.lua.throw_error("[push_nameproperty] Unhandled Operation"); + params.throw_error("push_nameproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_nameproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_nameproperty", "Operation not supported"); } auto push_textproperty(const PusherParams& params) -> void @@ -1064,7 +1071,7 @@ namespace RC::LuaType Unreal::FText* text = static_cast(params.data); if (!text) { - params.lua.throw_error("[push_textproperty] data pointer is nullptr"); + params.throw_error("push_textproperty", "data pointer is nullptr"); } switch (params.operation) @@ -1082,11 +1089,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); return; default: - params.lua.throw_error("[push_textproperty] Unhandled Operation"); + params.throw_error("push_textproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_textproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_textproperty", "Operation not supported"); } auto push_strproperty(const PusherParams& params) -> void @@ -1094,7 +1101,7 @@ namespace RC::LuaType Unreal::FString* string = static_cast(params.data); if (!string) { - params.lua.throw_error("[push_strproperty] data pointer is nullptr"); + params.throw_error("push_strproperty", "data pointer is nullptr"); } switch (params.operation) @@ -1117,7 +1124,7 @@ namespace RC::LuaType } else { - params.lua.throw_error("[push_strproperty] StrProperty can only be set to a string or FString"); + params.throw_error("push_strproperty", "StrProperty can only be set to a string or FString"); } return; } @@ -1125,11 +1132,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); return; default: - params.lua.throw_error("[push_strproperty] Unhandled Operation"); + params.throw_error("push_strproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_strproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_strproperty", "Operation not supported"); } auto push_softclassproperty(const PusherParams& params) -> void @@ -1137,7 +1144,7 @@ namespace RC::LuaType auto soft_ptr = static_cast(params.data); if (!soft_ptr) { - params.lua.throw_error("[push_softclassproperty] data pointer is nullptr"); + params.throw_error("push_softclassproperty", "data pointer is nullptr"); } switch (params.operation) @@ -1155,11 +1162,11 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); return; default: - params.lua.throw_error("[push_softclassproperty] Unhandled Operation"); + params.throw_error("push_softclassproperty", "Unhandled Operation"); break; } - params.lua.throw_error(fmt::format("[push_softclassproperty] Unknown Operation ({}) not supported", static_cast(params.operation))); + params.throw_error("push_softclassproperty", "Operation not supported"); } auto push_interfaceproperty(const PusherParams& params) -> void @@ -1167,7 +1174,7 @@ namespace RC::LuaType auto property_value = static_cast(params.data); if (!property_value) { - params.lua.throw_error("[push_interfaceproperty] data pointer is nullptr"); + params.throw_error("push_interfaceproperty", "data pointer is nullptr"); } // Finally construct the Lua object @@ -1189,7 +1196,7 @@ namespace RC::LuaType } else { - params.lua.throw_error("[push_interfaceproperty] Value must be UInterface or nil"); + params.throw_error("push_interfaceproperty", "Value must be UInterface or nil"); } break; } @@ -1197,7 +1204,7 @@ namespace RC::LuaType RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property); break; default: - params.lua.throw_error("[push_interfaceproperty] Unhandled Operation"); + params.throw_error("push_interfaceproperty", "Operation not supported"); break; } } From 7867897a84dde90206aaf4ee514dee778b025e7e Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 14 Dec 2024 17:53:30 +0100 Subject: [PATCH 6/6] feat(Lua): Property pushers now dump the Lua stack on error --- UE4SS/include/LuaType/LuaUObject.hpp | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/UE4SS/include/LuaType/LuaUObject.hpp b/UE4SS/include/LuaType/LuaUObject.hpp index c78704f57..39b702317 100644 --- a/UE4SS/include/LuaType/LuaUObject.hpp +++ b/UE4SS/include/LuaType/LuaUObject.hpp @@ -251,6 +251,42 @@ namespace RC::LuaType return "UnknownOperation"; } + // https://stackoverflow.com/questions/59091462/from-c-how-can-i-print-the-contents-of-the-lua-stack/59097940#59097940 + auto get_stack_dump(const char* message = "") const -> std::string + { + auto lua_state = lua.get_lua_state(); + auto out_message = fmt::format("\n\nLUA Stack dump -> START------------------------------\n{}\n", message); + int top = lua_gettop(lua_state); + for (int i = 1; i <= top; i++) + { + out_message.append(fmt::format("{}\t{}\t", i, luaL_typename(lua_state, i))); + switch (lua_type(lua_state, i)) + { + case LUA_TNUMBER: + out_message.append(fmt::format("{}", lua_tonumber(lua_state, i))); + break; + case LUA_TSTRING: + out_message.append(fmt::format("{}", lua_tostring(lua_state, i))); + break; + case LUA_TBOOLEAN: + out_message.append(fmt::format("{}", (lua_toboolean(lua_state, i) ? "true" : "false"))); + break; + case LUA_TNIL: + out_message.append("nil"); + break; + case LUA_TFUNCTION: + out_message.append("function"); + break; + default: + out_message.append(fmt::format("{}", lua_topointer(lua_state, i))); + break; + } + out_message.append("\n"); + } + out_message.append("\nLUA Stack dump -> END----------------------------\n\n"); + return out_message; + } + auto throw_error_internal_append_args(std::string&) const -> void { } @@ -288,6 +324,7 @@ namespace RC::LuaType error_message.append(fmt::format(" StoredAtIndex: {}\n", stored_at_index)); error_message.append(fmt::format(" CreateNewIfGetNonTrivialLocal: {}\n", create_new_if_get_non_trivial_local)); throw_error_internal_append_args(error_message, args...); + error_message.append(get_stack_dump()); lua.throw_error(error_message); } };