From 3c54c0eba22f205122933bd0570ac052a245af6f Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:40:00 +0200 Subject: [PATCH 01/16] Added 'RegisterHookFromBP' and 'UnregisterHookFromBP' to BPML_GenericFunctions --- .../BPML_GenericFunctions/Scripts/main.lua | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index 7e8017d43..65e695ace 100644 --- a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua +++ b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua @@ -65,3 +65,99 @@ RegisterCustomEvent("ConstructPersistentObject", function(ParamContext, ParamCla -- Return Value OutParam:set(PersistentObject) end) + +local RegisteredBPHooks = {} +---@param Context UObject +---@param HookName FString Full name of the function you want to register a hook for e.g /Script/Engine.PlayerController:ClientRestart +---@param FunctionToCall FString Name of the function you want to be called from your own actor whenever the hooked function fires +--- NOTE: Don't forget to unregister your hooks whenever your Actor gets destroyed. +RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionToCall) + -- Type checking similar to PrintToModLoader + if HookName:get():type() ~= "FString" then error(string.format("RegisterHookFromBP Param #1 must be FString but was %s", HookName:get():type())) end + if FunctionToCall:get():type() ~= "FString" then error(string.format("RegisterHookFromBP Param #2 must be FString but was %s", FunctionToCall:get():type())) end + + local HookName_AsString = HookName:get():ToString() + + -- Intended to be a safeguard for checking if a hook is actually valid, but is this really needed? + local HookFunctionObj = StaticFindObject(HookName_AsString) + if HookFunctionObj == nil or not HookFunctionObj:IsValid() or HookFunctionObj:type() ~= "UFunction" then + error("Tried to hook invalid function '" .. HookName_AsString .. "'") + end + + local FunctionName = FunctionToCall:get():ToString() + local Function = Context:get()[FunctionName] + if Function == nil or not Function:IsValid() or Function:type() ~= "UFunction" then + error("Function '" .. FunctionName .. "' doesn't exist in '" .. Context:get():GetFullName() .. "'") + end + + if RegisteredBPHooks[HookName_AsString] == nil then + RegisteredBPHooks[HookName_AsString] = {} + end + + local ClassFullName = Context:get():GetClass():GetFullName() + -- Prevent multiple hook registrations for a single function from the same Actor + if RegisteredBPHooks[HookName_AsString][ClassFullName] ~= nil then + UnregisterHook( + HookName_AsString, + RegisteredBPHooks[HookName_AsString][ClassFullName].PreHookId, + RegisteredBPHooks[HookName_AsString][ClassFullName].PostHookId + ) + end + + RegisteredBPHooks[HookName_AsString][ClassFullName] = { + Owner = Context:get(), + Function = Function, + PreHookId = 0, + PostHookId = 0 + } + + local PreHookId, PostHookId = RegisterHook(HookName_AsString, function (Context, ...) + for key, HookData in pairs(RegisteredBPHooks[HookName_AsString]) do + ---@class UObject + local Owner = HookData.Owner + if Owner ~= nil and Owner:IsValid() then + if HookData.Function ~= nil and HookData.Function:IsValid() then + local argTable = {} + local args = {...} + for i=1,#args do + table.insert(argTable, args[i]:get()) + end + + if #argTable > 0 then + HookData.Function(Owner, Context:get(), table.unpack(argTable, 1, #argTable)) + else + HookData.Function(Owner, Context:get()) + end + end + end + end + end) + + RegisteredBPHooks[HookName_AsString][ClassFullName].PreHookId = PreHookId + RegisteredBPHooks[HookName_AsString][ClassFullName].PostHookId = PostHookId +end) + +---@param HookName FString Full name of the function you want to unregister a hook for e.g /Script/Engine.PlayerController:ClientRestart +RegisterCustomEvent("UnregisterHookFromBP", function (Context, HookName) + if HookName:get():type() ~= "FString" then error(string.format("UnregisterHookFromBP Param #1 must be FString but was %s", HookName:get():type())) end + + local HookName_AsString = HookName:get():ToString() + + if RegisteredBPHooks[HookName_AsString] == nil then + return + end + + local ClassFullName = Context:get():GetClass():GetFullName() + + if RegisteredBPHooks[HookName_AsString][ClassFullName] == nil then + return + end + + UnregisterHook( + HookName_AsString, + RegisteredBPHooks[HookName_AsString][ClassFullName].PreHookId, + RegisteredBPHooks[HookName_AsString][ClassFullName].PostHookId + ) + + RegisteredBPHooks[HookName_AsString][ClassFullName] = nil +end) From 5fc6db451939bd09ef5c9db660e099d65bfa763c Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Sat, 2 Mar 2024 09:38:49 +0200 Subject: [PATCH 02/16] Add new IsA overload for Unreal property types --- UE4SS/src/LuaType/LuaXProperty.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index dc9944626..b07656537 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -178,6 +178,12 @@ No overload found for function 'IsA'. } return 1; } + else if (lua.is_userdata()) // FFieldClass, returned from Property:GetClass() + { + auto ffield_class = lua.get_userdata(); + lua.set_bool(lua_object.get_remote_cpp_object()->IsA(ffield_class.get_local_cpp_object())); + return 1; + } else { lua.throw_error(error_overload_not_found); From 7ba8a391b353a6f00df0d2724d9ba100510c282b Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Sat, 2 Mar 2024 09:43:29 +0200 Subject: [PATCH 03/16] Fix UFunction not calling 'setup_member_functions' for UStruct --- UE4SS/include/LuaType/LuaUFunction.hpp | 4 ++++ UE4SS/src/LuaType/LuaUFunction.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/UE4SS/include/LuaType/LuaUFunction.hpp b/UE4SS/include/LuaType/LuaUFunction.hpp index fe19aeccf..9d6b0f0db 100644 --- a/UE4SS/include/LuaType/LuaUFunction.hpp +++ b/UE4SS/include/LuaType/LuaUFunction.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace RC::Unreal { @@ -45,6 +46,9 @@ namespace RC::LuaType }; class UFunction : public UObjectBase { + public: + using Super = UStruct; + private: // This is the 'this' pointer for this UFunction Unreal::UObject* m_base{}; diff --git a/UE4SS/src/LuaType/LuaUFunction.cpp b/UE4SS/src/LuaType/LuaUFunction.cpp index 76118829a..55dcee377 100644 --- a/UE4SS/src/LuaType/LuaUFunction.cpp +++ b/UE4SS/src/LuaType/LuaUFunction.cpp @@ -75,6 +75,8 @@ namespace RC::LuaType template auto UFunction::setup_member_functions(const LuaMadeSimple::Lua::Table& table) -> void { + Super::setup_member_functions(table); + table.add_pair("GetFunctionFlags", [](const LuaMadeSimple::Lua& lua) -> int { const auto& lua_object = lua.get_userdata(); lua.set_integer(lua_object.get_remote_cpp_object()->GetFunctionFlags()); From 8962d8e841823802573457795229bb823c8badd9 Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Sat, 6 Apr 2024 02:45:36 +0300 Subject: [PATCH 04/16] Added 'HasAnyPropertyFlags' to Lua --- UE4SS/src/LuaType/LuaXProperty.cpp | 18 ++++ UE4SS/src/Mod/LuaMod.cpp | 82 +++++++++++++++++++ .../include/LuaMadeSimple/LuaMadeSimple.hpp | 4 + 3 files changed, 104 insertions(+) diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index b07656537..e107fc6da 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -137,6 +137,24 @@ namespace RC::LuaType return 1; }); + table.add_pair("HasAnyPropertyFlags", [](const LuaMadeSimple::Lua& lua) -> int { + std::string error_overload_not_found{R"( +No overload found for function 'Property.HasAnyPropertyFlags'. +Overloads: +#1: HasAnyPropertyFlags(EPropertyFlags PropertyFlags))"}; + + const auto& lua_object = lua.get_userdata(); + + if (!lua.is_integer()) + { + lua.throw_error(error_overload_not_found); + } + + Unreal::EPropertyFlags object_flags = static_cast(lua.get_integer()); + lua.set_bool(lua_object.get_remote_cpp_object()->HasAnyPropertyFlags(object_flags)); + return 1; + }); + table.add_pair("IsA", [](const LuaMadeSimple::Lua& lua) -> int { std::string error_overload_not_found{R"( No overload found for function 'IsA'. diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index e26abbf74..18d232085 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -617,6 +617,86 @@ namespace RC object_internal_flags_table.make_global("EInternalObjectFlags"); } + static auto register_property_flags(const LuaMadeSimple::Lua& lua) -> void + { + LuaMadeSimple::Lua::Table property_flags_table = lua.prepare_new_table(); + property_flags_table.add_pair("CPF_None", static_cast>(Unreal::EPropertyFlags::CPF_None)); + property_flags_table.add_pair("CPF_Edit", static_cast>(Unreal::EPropertyFlags::CPF_Edit)); + property_flags_table.add_pair("CPF_ConstParm", static_cast>(Unreal::EPropertyFlags::CPF_ConstParm)); + property_flags_table.add_pair("CPF_BlueprintVisible", + static_cast>(Unreal::EPropertyFlags::CPF_BlueprintVisible)); + property_flags_table.add_pair("CPF_ExportObject", static_cast>(Unreal::EPropertyFlags::CPF_ExportObject)); + property_flags_table.add_pair("CPF_BlueprintReadOnly", + static_cast>(Unreal::EPropertyFlags::CPF_BlueprintReadOnly)); + property_flags_table.add_pair("CPF_Net", static_cast>(Unreal::EPropertyFlags::CPF_Net)); + property_flags_table.add_pair("CPF_EditFixedSize", static_cast>(Unreal::EPropertyFlags::CPF_EditFixedSize)); + property_flags_table.add_pair("CPF_Parm", static_cast>(Unreal::EPropertyFlags::CPF_Parm)); + property_flags_table.add_pair("CPF_OutParm", static_cast>(Unreal::EPropertyFlags::CPF_OutParm)); + property_flags_table.add_pair("CPF_ZeroConstructor", + static_cast>(Unreal::EPropertyFlags::CPF_ZeroConstructor)); + property_flags_table.add_pair("CPF_ReturnParm", static_cast>(Unreal::EPropertyFlags::CPF_ReturnParm)); + property_flags_table.add_pair("CPF_DisableEditOnTemplate", + static_cast>(Unreal::EPropertyFlags::CPF_DisableEditOnTemplate)); + property_flags_table.add_pair("CPF_Transient", static_cast>(Unreal::EPropertyFlags::CPF_Transient)); + property_flags_table.add_pair("CPF_Config", static_cast>(Unreal::EPropertyFlags::CPF_Config)); + property_flags_table.add_pair("CPF_DisableEditOnInstance", + static_cast>(Unreal::EPropertyFlags::CPF_DisableEditOnInstance)); + property_flags_table.add_pair("CPF_EditConst", static_cast>(Unreal::EPropertyFlags::CPF_EditConst)); + property_flags_table.add_pair("CPF_GlobalConfig", static_cast>(Unreal::EPropertyFlags::CPF_GlobalConfig)); + property_flags_table.add_pair("CPF_InstancedReference", + static_cast>(Unreal::EPropertyFlags::CPF_InstancedReference)); + property_flags_table.add_pair("CPF_DuplicateTransient", + static_cast>(Unreal::EPropertyFlags::CPF_DuplicateTransient)); + property_flags_table.add_pair("CPF_SaveGame", static_cast>(Unreal::EPropertyFlags::CPF_SaveGame)); + property_flags_table.add_pair("CPF_NoClear", static_cast>(Unreal::EPropertyFlags::CPF_NoClear)); + property_flags_table.add_pair("CPF_ReferenceParm", static_cast>(Unreal::EPropertyFlags::CPF_ReferenceParm)); + property_flags_table.add_pair("CPF_BlueprintAssignable", + static_cast>(Unreal::EPropertyFlags::CPF_BlueprintAssignable)); + property_flags_table.add_pair("CPF_Deprecated", static_cast>(Unreal::EPropertyFlags::CPF_Deprecated)); + property_flags_table.add_pair("CPF_IsPlainOldData", + static_cast>(Unreal::EPropertyFlags::CPF_IsPlainOldData)); + property_flags_table.add_pair("CPF_RepSkip", static_cast>(Unreal::EPropertyFlags::CPF_RepSkip)); + property_flags_table.add_pair("CPF_RepNotify", static_cast>(Unreal::EPropertyFlags::CPF_RepNotify)); + property_flags_table.add_pair("CPF_Interp", static_cast>(Unreal::EPropertyFlags::CPF_Interp)); + property_flags_table.add_pair("CPF_NonTransactional", + static_cast>(Unreal::EPropertyFlags::CPF_NonTransactional)); + property_flags_table.add_pair("CPF_EditorOnly", static_cast>(Unreal::EPropertyFlags::CPF_EditorOnly)); + property_flags_table.add_pair("CPF_NoDestructor", static_cast>(Unreal::EPropertyFlags::CPF_NoDestructor)); + property_flags_table.add_pair("CPF_AutoWeak", static_cast>(Unreal::EPropertyFlags::CPF_AutoWeak)); + property_flags_table.add_pair("CPF_ContainsInstancedReference", + static_cast>(Unreal::EPropertyFlags::CPF_ContainsInstancedReference)); + property_flags_table.add_pair("CPF_AssetRegistrySearchable", + static_cast>(Unreal::EPropertyFlags::CPF_AssetRegistrySearchable)); + property_flags_table.add_pair("CPF_SimpleDisplay", static_cast>(Unreal::EPropertyFlags::CPF_SimpleDisplay)); + property_flags_table.add_pair("CPF_AdvancedDisplay", + static_cast>(Unreal::EPropertyFlags::CPF_AdvancedDisplay)); + property_flags_table.add_pair("CPF_Protected", static_cast>(Unreal::EPropertyFlags::CPF_Protected)); + property_flags_table.add_pair("CPF_BlueprintCallable", + static_cast>(Unreal::EPropertyFlags::CPF_BlueprintCallable)); + property_flags_table.add_pair("CPF_BlueprintAuthorityOnly", + static_cast>(Unreal::EPropertyFlags::CPF_BlueprintAuthorityOnly)); + property_flags_table.add_pair("CPF_TextExportTransient", + static_cast>(Unreal::EPropertyFlags::CPF_TextExportTransient)); + property_flags_table.add_pair("CPF_NonPIEDuplicateTransient", + static_cast>(Unreal::EPropertyFlags::CPF_NonPIEDuplicateTransient)); + property_flags_table.add_pair("CPF_ExposeOnSpawn", static_cast>(Unreal::EPropertyFlags::CPF_ExposeOnSpawn)); + property_flags_table.add_pair("CPF_PersistentInstance", + static_cast>(Unreal::EPropertyFlags::CPF_PersistentInstance)); + property_flags_table.add_pair("CPF_UObjectWrapper", + static_cast>(Unreal::EPropertyFlags::CPF_UObjectWrapper)); + property_flags_table.add_pair("CPF_HasGetValueTypeHash", + static_cast>(Unreal::EPropertyFlags::CPF_HasGetValueTypeHash)); + property_flags_table.add_pair("CPF_NativeAccessSpecifierPublic", + static_cast>(Unreal::EPropertyFlags::CPF_NativeAccessSpecifierPublic)); + property_flags_table.add_pair("CPF_NativeAccessSpecifierProtected", + static_cast>(Unreal::EPropertyFlags::CPF_NativeAccessSpecifierProtected)); + property_flags_table.add_pair("CPF_NativeAccessSpecifierPrivate", + static_cast>(Unreal::EPropertyFlags::CPF_NativeAccessSpecifierPrivate)); + property_flags_table.add_pair("CPF_SkipSerialization", + static_cast>(Unreal::EPropertyFlags::CPF_SkipSerialization)); + property_flags_table.make_global("EPropertyFlags"); + } + static auto register_efindname(const LuaMadeSimple::Lua& lua) -> void { LuaMadeSimple::Lua::Table efindname_table = lua.prepare_new_table(); @@ -3300,6 +3380,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. register_all_property_types(lua); register_object_flags(lua); + register_property_flags(lua); register_efindname(lua); lua.set_nil(); @@ -3449,6 +3530,7 @@ No overload found for function 'FPackageName:IsValidLongPackageName'. register_input_globals(*LuaStatics::console_executor); register_all_property_types(*LuaStatics::console_executor); register_object_flags(*LuaStatics::console_executor); + register_property_flags(*LuaStatics::console_executor); LuaStatics::console_executor_enabled = true; }; diff --git a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp index a4ca10f2f..4f7b3a218 100644 --- a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp +++ b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp @@ -296,6 +296,10 @@ namespace RC::LuaMadeSimple { lua_pushinteger(get_lua_instance().get_lua_state(), value); } + else if constexpr (std::is_same_v) + { + lua_pushinteger(get_lua_instance().get_lua_state(), static_cast(value)); + } else if constexpr (std::is_same_v) { lua_pushinteger(get_lua_instance().get_lua_state(), static_cast(value)); From d1b2bda21b1499ac2e9390ff3554c0287d5abe9f Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 2 Mar 2024 19:48:28 +0100 Subject: [PATCH 05/16] Added 'NumericProperty' to Lua. --- UE4SS/include/LuaType/LuaXNumericProperty.hpp | 39 ++++++++++ UE4SS/src/LuaType/LuaXNumericProperty.cpp | 76 +++++++++++++++++++ UE4SS/src/LuaType/LuaXProperty.cpp | 6 ++ assets/Changelog.md | 1 + docs/SUMMARY.md | 1 + docs/lua-api/classes/numericproperty.md | 11 +++ 6 files changed, 134 insertions(+) create mode 100644 UE4SS/include/LuaType/LuaXNumericProperty.hpp create mode 100644 UE4SS/src/LuaType/LuaXNumericProperty.cpp create mode 100644 docs/lua-api/classes/numericproperty.md diff --git a/UE4SS/include/LuaType/LuaXNumericProperty.hpp b/UE4SS/include/LuaType/LuaXNumericProperty.hpp new file mode 100644 index 000000000..5cc069112 --- /dev/null +++ b/UE4SS/include/LuaType/LuaXNumericProperty.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace RC::Unreal +{ + class FNumericProperty; +} + +namespace RC::LuaType +{ + struct FNumericPropertyName + { + constexpr static const char* ToString() + { + return "NumericProperty"; + } + }; + class XNumericProperty : public RemoteObjectBase + { + public: + using Super = XProperty; + + private: + explicit XNumericProperty(Unreal::FNumericProperty* object); + + public: + XNumericProperty() = delete; + auto static construct(const LuaMadeSimple::Lua&, Unreal::FNumericProperty*) -> const LuaMadeSimple::Lua::Table; + auto static construct(const LuaMadeSimple::Lua&, BaseObject&) -> const LuaMadeSimple::Lua::Table; + + private: + auto static setup_metamethods(BaseObject&) -> void; + + private: + template + auto static setup_member_functions(const LuaMadeSimple::Lua::Table&) -> void; + }; +} // namespace RC::LuaType diff --git a/UE4SS/src/LuaType/LuaXNumericProperty.cpp b/UE4SS/src/LuaType/LuaXNumericProperty.cpp new file mode 100644 index 000000000..79b760523 --- /dev/null +++ b/UE4SS/src/LuaType/LuaXNumericProperty.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#pragma warning(disable : 4005) +#include +#pragma warning(default : 4005) + +namespace RC::LuaType +{ + XNumericProperty::XNumericProperty(Unreal::FNumericProperty* object) : RemoteObjectBase(object) + { + } + + auto XNumericProperty::construct(const LuaMadeSimple::Lua& lua, Unreal::FNumericProperty* unreal_object) -> const LuaMadeSimple::Lua::Table + { + LuaType::XNumericProperty lua_object{unreal_object}; + + auto metatable_name = ClassName::ToString(); + + LuaMadeSimple::Lua::Table table = lua.get_metatable(metatable_name); + if (lua.is_nil(-1)) + { + lua.discard_value(-1); + LuaMadeSimple::Type::RemoteObject::construct(lua, lua_object); + setup_metamethods(lua_object); + setup_member_functions(table); + lua.new_metatable(metatable_name, lua_object.get_metamethods()); + } + + // Create object & surrender ownership to Lua + lua.transfer_stack_object(std::move(lua_object), metatable_name, lua_object.get_metamethods()); + + return table; + } + + auto XNumericProperty::construct(const LuaMadeSimple::Lua& lua, BaseObject& construct_to) -> const LuaMadeSimple::Lua::Table + { + LuaMadeSimple::Lua::Table table = LuaMadeSimple::Type::RemoteObject::construct(lua, construct_to); + + setup_member_functions(table); + + setup_metamethods(construct_to); + + return table; + } + + auto XNumericProperty::setup_metamethods([[maybe_unused]] BaseObject& base_object) -> void + { + // XNumericProperty has no metamethods + } + + template + auto XNumericProperty::setup_member_functions(const LuaMadeSimple::Lua::Table& table) -> void + { + Super::setup_member_functions(table); + + table.add_pair("IsFloatingPoint", [](const LuaMadeSimple::Lua& lua) -> int { + const auto& lua_object = lua.get_userdata(); + lua.set_bool(lua_object.get_remote_cpp_object()->IsFloatingPoint()); + return 1; + }); + + + if constexpr (is_final == LuaMadeSimple::Type::IsFinal::Yes) + { + table.add_pair("type", [](const LuaMadeSimple::Lua& lua) -> int { + lua.set_string(ClassName::ToString()); + return 1; + }); + + // If this is the final object then we also want to finalize creating the table + // If not then it's the responsibility of the overriding object to call 'make_global()' + // table.make_global(ClassName::ToString()); + } + } +} // namespace RC::LuaType diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index e107fc6da..20b2ab680 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #pragma warning(disable : 4005) #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #pragma warning(default : 4005) namespace RC::LuaType @@ -45,6 +47,10 @@ namespace RC::LuaType { XArrayProperty::construct(lua, as_array_property); } + else if (auto* as_numeric_proprety = Unreal::CastField(property); as_numeric_proprety) + { + XNumericProperty::construct(lua, as_numeric_proprety); + } else { XProperty::construct(lua, property); diff --git a/assets/Changelog.md b/assets/Changelog.md index e0a8cac07..9d1ee0b12 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -11,6 +11,7 @@ TBD ### UHT Dumper ### Lua API +Implemented `NumericProperty`, and its `IsFloatingPoint` member function. ### C++ API diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 091d821b0..179f6ef11 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -45,6 +45,7 @@ - [StructProperty](./lua-api/classes/structproperty.md) - [BoolProperty](./lua-api/classes/boolproperty.md) - [ArrayProperty](./lua-api/classes/arrayproperty.md) + - [NumericProperty](./lua-api/classes/numericproperty.md) - [UObjectReflection](./lua-api/classes/uobjectreflection.md) - [FOutputDevice](./lua-api/classes/foutputdevice.md) - [FWeakObjectPtr](./lua-api/classes/fweakobjectptr.md) diff --git a/docs/lua-api/classes/numericproperty.md b/docs/lua-api/classes/numericproperty.md new file mode 100644 index 000000000..4435f31e0 --- /dev/null +++ b/docs/lua-api/classes/numericproperty.md @@ -0,0 +1,11 @@ +# BoolProperty + +## Inheritance +[Property](./property.md) + +## Methods + +### IsFloatingPoint() + +- **Return type:** `bool` +- **Returns:**` Whether this NumericProperty represents a floating point number. \ No newline at end of file From ad4388c501dcca02680f023662e3e027d3b48b2d Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 2 Mar 2024 20:04:44 +0100 Subject: [PATCH 06/16] Fixed typos. --- UE4SS/src/LuaType/LuaXProperty.cpp | 4 ++-- docs/lua-api/classes/numericproperty.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index 20b2ab680..9d3ec778a 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -47,9 +47,9 @@ namespace RC::LuaType { XArrayProperty::construct(lua, as_array_property); } - else if (auto* as_numeric_proprety = Unreal::CastField(property); as_numeric_proprety) + else if (auto* as_numeric_property = Unreal::CastField(property); as_numeric_property) { - XNumericProperty::construct(lua, as_numeric_proprety); + XNumericProperty::construct(lua, as_numeric_property); } else { diff --git a/docs/lua-api/classes/numericproperty.md b/docs/lua-api/classes/numericproperty.md index 4435f31e0..58ac64398 100644 --- a/docs/lua-api/classes/numericproperty.md +++ b/docs/lua-api/classes/numericproperty.md @@ -1,4 +1,4 @@ -# BoolProperty +# NumericProperty ## Inheritance [Property](./property.md) From 5c5e8378b09ff2d273c8f05854558bce77e7b65c Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Thu, 11 Apr 2024 08:00:30 +0300 Subject: [PATCH 07/16] Finish up work on EPropertyFlags --- UE4SS/src/LuaType/LuaXProperty.cpp | 18 ++++++ assets/Changelog.md | 2 + assets/Mods/shared/Types.lua | 55 ++++++++++++++++++ docs/lua-api/classes/property.md | 10 ++++ .../table-definitions/epropertyflags.md | 56 +++++++++++++++++++ 5 files changed, 141 insertions(+) create mode 100644 docs/lua-api/table-definitions/epropertyflags.md diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index 9d3ec778a..72333152c 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -161,6 +161,24 @@ No overload found for function 'Property.HasAnyPropertyFlags'. return 1; }); + table.add_pair("HasAllPropertyFlags", [](const LuaMadeSimple::Lua& lua) -> int { + std::string error_overload_not_found{R"( +No overload found for function 'Property.HasAllPropertyFlags'. +Overloads: +#1: HasAllPropertyFlags(EPropertyFlags PropertyFlags))"}; + + const auto& lua_object = lua.get_userdata(); + + if (!lua.is_integer()) + { + lua.throw_error(error_overload_not_found); + } + + Unreal::EPropertyFlags object_flags = static_cast(lua.get_integer()); + lua.set_bool(lua_object.get_remote_cpp_object()->HasAllPropertyFlags(object_flags)); + return 1; + }); + table.add_pair("IsA", [](const LuaMadeSimple::Lua& lua) -> int { std::string error_overload_not_found{R"( No overload found for function 'IsA'. diff --git a/assets/Changelog.md b/assets/Changelog.md index 9d1ee0b12..447608e19 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -13,6 +13,8 @@ TBD ### Lua API Implemented `NumericProperty`, and its `IsFloatingPoint` member function. +Implemented member functions `HasAnyPropertyFlags` and `HasAllPropertyFlags` for Property. + ### C++ API ### Experimental diff --git a/assets/Mods/shared/Types.lua b/assets/Mods/shared/Types.lua index f8109c247..54b64fe60 100644 --- a/assets/Mods/shared/Types.lua +++ b/assets/Mods/shared/Types.lua @@ -243,6 +243,61 @@ EObjectFlags = { RF_AllFlags = 0x0FFFFFFF, } +----A table of property flags that can be or'd together by using |. +---@enum EPropertyFlags +EPropertyFlags = { + CPF_None = 0, + CPF_Edit = 0x0000000000000001, + CPF_ConstParm = 0x0000000000000002, + CPF_BlueprintVisible = 0x0000000000000004, + CPF_ExportObject = 0x0000000000000008, + CPF_BlueprintReadOnly = 0x0000000000000010, + CPF_Net = 0x0000000000000020, + CPF_EditFixedSize = 0x0000000000000040, + CPF_Parm = 0x0000000000000080, + CPF_OutParm = 0x0000000000000100, + CPF_ZeroConstructor = 0x0000000000000200, + CPF_ReturnParm = 0x0000000000000400, + CPF_DisableEditOnTemplate = 0x0000000000000800, + CPF_Transient = 0x0000000000002000, + CPF_Config = 0x0000000000004000, + CPF_DisableEditOnInstance = 0x0000000000010000, + CPF_EditConst = 0x0000000000020000, + CPF_GlobalConfig = 0x0000000000040000, + CPF_InstancedReference = 0x0000000000080000, + CPF_DuplicateTransient = 0x0000000000200000, + CPF_SaveGame = 0x0000000001000000, + CPF_NoClear = 0x0000000002000000, + CPF_ReferenceParm = 0x0000000008000000, + CPF_BlueprintAssignable = 0x0000000010000000, + CPF_Deprecated = 0x0000000020000000, + CPF_IsPlainOldData = 0x0000000040000000, + CPF_RepSkip = 0x0000000080000000, + CPF_RepNotify = 0x0000000100000000, + CPF_Interp = 0x0000000200000000, + CPF_NonTransactional = 0x0000000400000000, + CPF_EditorOnly = 0x0000000800000000, + CPF_NoDestructor = 0x0000001000000000, + CPF_AutoWeak = 0x0000004000000000, + CPF_ContainsInstancedReference = 0x0000008000000000, + CPF_AssetRegistrySearchable = 0x0000010000000000, + CPF_SimpleDisplay = 0x0000020000000000, + CPF_AdvancedDisplay = 0x0000040000000000, + CPF_Protected = 0x0000080000000000, + CPF_BlueprintCallable = 0x0000100000000000, + CPF_BlueprintAuthorityOnly = 0x0000200000000000, + CPF_TextExportTransient = 0x0000400000000000, + CPF_NonPIEDuplicateTransient = 0x0000800000000000, + CPF_ExposeOnSpawn = 0x0001000000000000, + CPF_PersistentInstance = 0x0002000000000000, + CPF_UObjectWrapper = 0x0004000000000000, + CPF_HasGetValueTypeHash = 0x0008000000000000, + CPF_NativeAccessSpecifierPublic = 0x0010000000000000, + CPF_NativeAccessSpecifierProtected = 0x0020000000000000, + CPF_NativeAccessSpecifierPrivate = 0x0040000000000000, + CPF_SkipSerialization = 0x0080000000000000, +} + ---@enum ModifierKey ModifierKey = { SHIFT = 0x10, diff --git a/docs/lua-api/classes/property.md b/docs/lua-api/classes/property.md index 98e3dd05a..268597bf2 100644 --- a/docs/lua-api/classes/property.md +++ b/docs/lua-api/classes/property.md @@ -21,6 +21,16 @@ - **Return type:** `bool` - **Returns:** `true` if the property is of type `PropertyType`. +### HasAllPropertyFlags(EPropertyFlags FlagsToCheck) + +- **Return type:** `bool` +- **Returns:** whether the property has all of the specified flags. + +### HasAnyPropertyFlags(EPropertyFlags FlagsToCheck) + +- **Return type:** `bool` +- **Returns:** whether the property has any of the specified flags. + ### GetClass() - **Return type:** `PropertyClass` diff --git a/docs/lua-api/table-definitions/epropertyflags.md b/docs/lua-api/table-definitions/epropertyflags.md new file mode 100644 index 000000000..49f713482 --- /dev/null +++ b/docs/lua-api/table-definitions/epropertyflags.md @@ -0,0 +1,56 @@ +# EPropertyFlags + +A table of property flags that can be or'd together by using `|` + +Field Name | Field Value Type +--------- | -------------- +CPF_None | 0 +CPF_Edit | 0x0000000000000001 +CPF_ConstParm | 0x0000000000000002 +CPF_BlueprintVisible | 0x0000000000000004 +CPF_ExportObject | 0x0000000000000008 +CPF_BlueprintReadOnly | 0x0000000000000010 +CPF_Net | 0x0000000000000020 +CPF_EditFixedSize | 0x0000000000000040 +CPF_Parm | 0x0000000000000080 +CPF_OutParm | 0x0000000000000100 +CPF_ZeroConstructor | 0x0000000000000200 +CPF_ReturnParm | 0x0000000000000400 +CPF_DisableEditOnTemplate | 0x0000000000000800 +CPF_Transient | 0x0000000000002000 +CPF_Config | 0x0000000000004000 +CPF_DisableEditOnInstance | 0x0000000000010000 +CPF_EditConst | 0x0000000000020000 +CPF_GlobalConfig | 0x0000000000040000 +CPF_InstancedReference | 0x0000000000080000 +CPF_DuplicateTransient | 0x0000000000200000 +CPF_SaveGame | 0x0000000001000000 +CPF_NoClear | 0x0000000002000000 +CPF_ReferenceParm | 0x0000000008000000 +CPF_BlueprintAssignable | 0x0000000010000000 +CPF_Deprecated | 0x0000000020000000 +CPF_IsPlainOldData | 0x0000000040000000 +CPF_RepSkip | 0x0000000080000000 +CPF_RepNotify | 0x0000000100000000 +CPF_Interp | 0x0000000200000000 +CPF_NonTransactional | 0x0000000400000000 +CPF_EditorOnly | 0x0000000800000000 +CPF_NoDestructor | 0x0000001000000000 +CPF_AutoWeak | 0x0000004000000000 +CPF_ContainsInstancedReference | 0x0000008000000000 +CPF_AssetRegistrySearchable | 0x0000010000000000 +CPF_SimpleDisplay | 0x0000020000000000 +CPF_AdvancedDisplay | 0x0000040000000000 +CPF_Protected | 0x0000080000000000 +CPF_BlueprintCallable | 0x0000100000000000 +CPF_BlueprintAuthorityOnly | 0x0000200000000000 +CPF_TextExportTransient | 0x0000400000000000 +CPF_NonPIEDuplicateTransient | 0x0000800000000000 +CPF_ExposeOnSpawn | 0x0001000000000000 +CPF_PersistentInstance | 0x0002000000000000 +CPF_UObjectWrapper | 0x0004000000000000 +CPF_HasGetValueTypeHash | 0x0008000000000000 +CPF_NativeAccessSpecifierPublic | 0x0010000000000000 +CPF_NativeAccessSpecifierProtected | 0x0020000000000000 +CPF_NativeAccessSpecifierPrivate | 0x0040000000000000 +CPF_SkipSerialization | 0x0080000000000000 \ No newline at end of file From 468932ed929d0537eb027d5d3e5b9cae3cb8b0a9 Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Thu, 11 Apr 2024 08:41:37 +0300 Subject: [PATCH 08/16] Add type-checking --- .../BPML_GenericFunctions/Scripts/main.lua | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index 65e695ace..753cd81c3 100644 --- a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua +++ b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua @@ -78,22 +78,71 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT local HookName_AsString = HookName:get():ToString() - -- Intended to be a safeguard for checking if a hook is actually valid, but is this really needed? - local HookFunctionObj = StaticFindObject(HookName_AsString) - if HookFunctionObj == nil or not HookFunctionObj:IsValid() or HookFunctionObj:type() ~= "UFunction" then - error("Tried to hook invalid function '" .. HookName_AsString .. "'") + -- Safeguard for checking if the function hook target is actually valid + local OriginalFunction = StaticFindObject(HookName_AsString) + if OriginalFunction == nil or not OriginalFunction:IsValid() or OriginalFunction:type() ~= "UFunction" then + error(string.format("Tried to hook invalid function '%s'", HookName_AsString)) end local FunctionName = FunctionToCall:get():ToString() - local Function = Context:get()[FunctionName] - if Function == nil or not Function:IsValid() or Function:type() ~= "UFunction" then - error("Function '" .. FunctionName .. "' doesn't exist in '" .. Context:get():GetFullName() .. "'") + local CallbackFunction = Context:get()[FunctionName] + if CallbackFunction == nil or not CallbackFunction:IsValid() or CallbackFunction:type() ~= "UFunction" then + error(string.format("Function '%s' doesn't exist in '%s'", FunctionName, Context:get():GetFullName())) end if RegisteredBPHooks[HookName_AsString] == nil then RegisteredBPHooks[HookName_AsString] = {} end + local ExpectedContextClass = OriginalFunction:GetOuter() + + if not ExpectedContextClass:IsValid() or not ExpectedContextClass:IsClass() then + error("Context Class was invalid or not a Class") + end + + local OriginalTypes = {} + local CallbackTypes = {} + + OriginalFunction:ForEachProperty(function(Property) + if Property:HasAnyPropertyFlags( + EPropertyFlags.CPF_ConstParm | + EPropertyFlags.CPF_Parm | + EPropertyFlags.CPF_OutParm | + EPropertyFlags.CPF_ReturnParm + ) then + table.insert(OriginalTypes, Property) + end + end) + + CallbackFunction:ForEachProperty(function(Property) + if Property:HasAnyPropertyFlags( + EPropertyFlags.CPF_ConstParm | + EPropertyFlags.CPF_Parm | + EPropertyFlags.CPF_OutParm | + EPropertyFlags.CPF_ReturnParm + ) then + table.insert(CallbackTypes, Property) + end + end) + + if #OriginalTypes+1 ~= #CallbackTypes then + error("Param count did not match!") + end + + if CallbackTypes[1]:GetPropertyClass():GetFullName() ~= ExpectedContextClass:GetFullName() then + error(string.format("Context Class did not match the expected Class '%s'", ExpectedContextClass:GetFName():ToString())) + end + + for i = 1, #OriginalTypes, 1 do + -- Using i+1 here to skip Context from our Callback function, maybe there's a cleaner/better way? + if ((CallbackTypes[i+1]:IsA(OriginalTypes[i]:GetClass())) or (type(OriginalTypes[i].IsFloatingPoint) == "function" and CallbackTypes[i+1]:IsFloatingPoint() and OriginalTypes[i]:IsFloatingPoint())) then + + else + error("Param #" .. i .. " did not match the expected type '" .. OriginalTypes[i]:GetClass():GetFName():ToString() .. + "' got '" .. CallbackTypes[i+1]:GetClass():GetFName():ToString() .. "'") + end + end + local ClassFullName = Context:get():GetClass():GetFullName() -- Prevent multiple hook registrations for a single function from the same Actor if RegisteredBPHooks[HookName_AsString][ClassFullName] ~= nil then @@ -106,7 +155,7 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT RegisteredBPHooks[HookName_AsString][ClassFullName] = { Owner = Context:get(), - Function = Function, + Function = CallbackFunction, PreHookId = 0, PostHookId = 0 } From 89cc5e4548a1a10d76e3ac9227e0d86adbd808b3 Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Thu, 11 Apr 2024 17:32:11 +0300 Subject: [PATCH 09/16] Switch to IsA comparison for functions --- assets/Mods/BPML_GenericFunctions/Scripts/main.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index 753cd81c3..5d1f3a2ef 100644 --- a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua +++ b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua @@ -5,6 +5,13 @@ local VerboseLogging = true +local FunctionClass = StaticFindObject("/Script/CoreUObject.Function") +if not FunctionClass:IsValid() then + -- No need to try again because the function class is guaranteed to exist when Lua mods are initialized. + -- If it doesn't exist, then something's gone horribly wrong or a massive engine change has occurred that removed/renamed this object. + error("/Script/CoreUObject.Function not found!") +end + local function Log(Message, AlwaysLog) if not VerboseLogging and not AlwaysLog then return end print(Message) @@ -80,13 +87,13 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT -- Safeguard for checking if the function hook target is actually valid local OriginalFunction = StaticFindObject(HookName_AsString) - if OriginalFunction == nil or not OriginalFunction:IsValid() or OriginalFunction:type() ~= "UFunction" then - error(string.format("Tried to hook invalid function '%s'", HookName_AsString)) + if not OriginalFunction:IsA(FunctionClass) then + error("Not a function!") end local FunctionName = FunctionToCall:get():ToString() local CallbackFunction = Context:get()[FunctionName] - if CallbackFunction == nil or not CallbackFunction:IsValid() or CallbackFunction:type() ~= "UFunction" then + if not CallbackFunction:IsA(FunctionClass) then error(string.format("Function '%s' doesn't exist in '%s'", FunctionName, Context:get():GetFullName())) end From 7e87fb1bcbad3c21461620d03e8be6fa54379b4d Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Thu, 11 Apr 2024 21:01:54 +0300 Subject: [PATCH 10/16] Rework type-checking --- .../BPML_GenericFunctions/Scripts/main.lua | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index 5d1f3a2ef..e65f06179 100644 --- a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua +++ b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua @@ -132,21 +132,38 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT end end) - if #OriginalTypes+1 ~= #CallbackTypes then - error("Param count did not match!") + if #CallbackTypes == 0 then + error("Callback function must have atleast one param for Context!") end - if CallbackTypes[1]:GetPropertyClass():GetFullName() ~= ExpectedContextClass:GetFullName() then - error(string.format("Context Class did not match the expected Class '%s'", ExpectedContextClass:GetFName():ToString())) + -- Check that the first callback param matches the expected context class + if not CallbackTypes[1]:IsA(PropertyTypes.ObjectProperty) or CallbackTypes[1]:GetPropertyClass():GetFullName() ~= ExpectedContextClass:GetFullName() then + error(string.format("Expected '%s' as the Context Class, got '%s' instead!", ExpectedContextClass:GetFName():ToString(), + CallbackTypes[1]:GetClass():GetFName():ToString())) end - for i = 1, #OriginalTypes, 1 do - -- Using i+1 here to skip Context from our Callback function, maybe there's a cleaner/better way? - if ((CallbackTypes[i+1]:IsA(OriginalTypes[i]:GetClass())) or (type(OriginalTypes[i].IsFloatingPoint) == "function" and CallbackTypes[i+1]:IsFloatingPoint() and OriginalTypes[i]:IsFloatingPoint())) then - - else - error("Param #" .. i .. " did not match the expected type '" .. OriginalTypes[i]:GetClass():GetFName():ToString() .. - "' got '" .. CallbackTypes[i+1]:GetClass():GetFName():ToString() .. "'") + -- Remove Context from the table since we're done with it now + table.remove(CallbackTypes, 1) + + if #OriginalTypes ~= #CallbackTypes then + error(string.format("Param count did not match, expected %d, but got %d!", #OriginalTypes + 1, #CallbackTypes + 1)) + end + + local WasParamInvalid = false + for i = 1, #CallbackTypes, 1 do + if not CallbackTypes[i]:IsA(OriginalTypes[i]:GetClass()) then + if type(CallbackTypes[i].IsFloatingPoint) ~= "function" then + WasParamInvalid = true + else + if not (CallbackTypes[i]:IsFloatingPoint() and OriginalTypes[i]:IsFloatingPoint()) then + WasParamInvalid = true + end + end + end + + if WasParamInvalid then + error(string.format("Param #%d did not match the expected type '%s' got '%s'", i + 1, OriginalTypes[i]:GetClass():GetFName():ToString(), + CallbackTypes[i]:GetClass():GetFName():ToString())) end end From 1c9cefb656b804f13489d21ac3cae8b7a8fd4551 Mon Sep 17 00:00:00 2001 From: UE4SS Date: Fri, 12 Apr 2024 07:18:48 +0200 Subject: [PATCH 11/16] Added custom UInt64 as a custom type to Lua Changed HasAll/AnyPropertyFlags to use this new type. Also changed the EPropertyFlags table to use this type. This required adding __bor and __band metamethods. --- UE4SS/include/LuaType/LuaUInt64.hpp | 28 ++++ UE4SS/src/LuaType/LuaUInt64.cpp | 44 ++++++ UE4SS/src/LuaType/LuaXProperty.cpp | 11 +- UE4SS/src/Mod/LuaMod.cpp | 131 ++++++++---------- .../include/LuaMadeSimple/LuaMadeSimple.hpp | 8 ++ .../first/LuaMadeSimple/src/LuaMadeSimple.cpp | 2 + 6 files changed, 146 insertions(+), 78 deletions(-) create mode 100644 UE4SS/include/LuaType/LuaUInt64.hpp create mode 100644 UE4SS/src/LuaType/LuaUInt64.cpp diff --git a/UE4SS/include/LuaType/LuaUInt64.hpp b/UE4SS/include/LuaType/LuaUInt64.hpp new file mode 100644 index 000000000..9fc2a4789 --- /dev/null +++ b/UE4SS/include/LuaType/LuaUInt64.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include + +namespace RC::LuaType +{ + struct UInt64Name + { + constexpr static const char* ToString() + { + return "UInt64"; + } + }; + class UInt64 : public LocalObjectBase + { + private: + explicit UInt64(uint64_t object); + + public: + UInt64() = delete; + auto static construct(const LuaMadeSimple::Lua&, uint64_t) -> const LuaMadeSimple::Lua::Table; + + private: + auto static setup_metamethods(BaseObject&) -> void; + }; +} // namespace RC::LuaType diff --git a/UE4SS/src/LuaType/LuaUInt64.cpp b/UE4SS/src/LuaType/LuaUInt64.cpp new file mode 100644 index 000000000..67e8e4f0c --- /dev/null +++ b/UE4SS/src/LuaType/LuaUInt64.cpp @@ -0,0 +1,44 @@ +#include + +namespace RC::LuaType +{ + UInt64::UInt64(uint64_t number) : LocalObjectBase(std::move(number)) + { + } + + auto UInt64::construct(const LuaMadeSimple::Lua& lua, uint64_t number) -> const LuaMadeSimple::Lua::Table + { + LuaType::UInt64 lua_object{number}; + + static constexpr auto metatable_name = ClassName::ToString(); + + auto table = lua.get_metatable(metatable_name); + if (lua.is_nil(-1)) + { + lua.discard_value(-1); + lua.prepare_new_table(); + setup_metamethods(lua_object); + lua.new_metatable(metatable_name, lua_object.get_metamethods()); + } + + lua.transfer_stack_object(std::move(lua_object), metatable_name, lua_object.get_metamethods()); + return LuaMadeSimple::Lua::Table{lua}; + } + + auto UInt64::setup_metamethods(BaseObject& base_object) -> void + { + base_object.get_metamethods().create(LuaMadeSimple::Lua::MetaMethod::BinaryAnd, [](const LuaMadeSimple::Lua& lua) -> int { + auto& lua_object = lua.get_userdata(); + auto& lua_object_other = lua.get_userdata(); + LuaType::UInt64::construct(lua, lua_object.get_local_cpp_object() & lua_object_other.get_local_cpp_object()); + return 1; + }); + + base_object.get_metamethods().create(LuaMadeSimple::Lua::MetaMethod::BinaryOr, [](const LuaMadeSimple::Lua& lua) -> int { + auto& lua_object = lua.get_userdata(); + auto& lua_object_other = lua.get_userdata(); + LuaType::UInt64::construct(lua, lua_object.get_local_cpp_object() | lua_object_other.get_local_cpp_object()); + return 1; + }); + } +} // namespace RC::LuaType diff --git a/UE4SS/src/LuaType/LuaXProperty.cpp b/UE4SS/src/LuaType/LuaXProperty.cpp index 72333152c..ca3e314fe 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #pragma warning(disable : 4005) #include #include @@ -151,12 +152,13 @@ No overload found for function 'Property.HasAnyPropertyFlags'. const auto& lua_object = lua.get_userdata(); - if (!lua.is_integer()) + if (!lua.is_userdata()) { lua.throw_error(error_overload_not_found); } - Unreal::EPropertyFlags object_flags = static_cast(lua.get_integer()); + auto& lua_uint64 = lua.get_userdata(); + auto object_flags = static_cast(lua_uint64.get_local_cpp_object()); lua.set_bool(lua_object.get_remote_cpp_object()->HasAnyPropertyFlags(object_flags)); return 1; }); @@ -169,12 +171,13 @@ No overload found for function 'Property.HasAllPropertyFlags'. const auto& lua_object = lua.get_userdata(); - if (!lua.is_integer()) + if (!lua.is_userdata()) { lua.throw_error(error_overload_not_found); } - Unreal::EPropertyFlags object_flags = static_cast(lua.get_integer()); + auto& lua_uint64 = lua.get_userdata(); + auto object_flags = static_cast(lua_uint64.get_local_cpp_object()); lua.set_bool(lua_object.get_remote_cpp_object()->HasAllPropertyFlags(object_flags)); return 1; }); diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index 18d232085..0ede4ac0b 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #pragma warning(disable : 4005) @@ -619,82 +620,64 @@ namespace RC static auto register_property_flags(const LuaMadeSimple::Lua& lua) -> void { +#define ADD_PROPERTY_FLAGS_PAIR(Key) \ + property_flags_table.add_key(#Key); \ + LuaType::UInt64::construct(lua, static_cast>(Unreal::EPropertyFlags::Key)); \ + property_flags_table.fuse_pair(); + LuaMadeSimple::Lua::Table property_flags_table = lua.prepare_new_table(); - property_flags_table.add_pair("CPF_None", static_cast>(Unreal::EPropertyFlags::CPF_None)); - property_flags_table.add_pair("CPF_Edit", static_cast>(Unreal::EPropertyFlags::CPF_Edit)); - property_flags_table.add_pair("CPF_ConstParm", static_cast>(Unreal::EPropertyFlags::CPF_ConstParm)); - property_flags_table.add_pair("CPF_BlueprintVisible", - static_cast>(Unreal::EPropertyFlags::CPF_BlueprintVisible)); - property_flags_table.add_pair("CPF_ExportObject", static_cast>(Unreal::EPropertyFlags::CPF_ExportObject)); - property_flags_table.add_pair("CPF_BlueprintReadOnly", - static_cast>(Unreal::EPropertyFlags::CPF_BlueprintReadOnly)); - property_flags_table.add_pair("CPF_Net", static_cast>(Unreal::EPropertyFlags::CPF_Net)); - property_flags_table.add_pair("CPF_EditFixedSize", static_cast>(Unreal::EPropertyFlags::CPF_EditFixedSize)); - property_flags_table.add_pair("CPF_Parm", static_cast>(Unreal::EPropertyFlags::CPF_Parm)); - property_flags_table.add_pair("CPF_OutParm", static_cast>(Unreal::EPropertyFlags::CPF_OutParm)); - property_flags_table.add_pair("CPF_ZeroConstructor", - static_cast>(Unreal::EPropertyFlags::CPF_ZeroConstructor)); - property_flags_table.add_pair("CPF_ReturnParm", static_cast>(Unreal::EPropertyFlags::CPF_ReturnParm)); - property_flags_table.add_pair("CPF_DisableEditOnTemplate", - static_cast>(Unreal::EPropertyFlags::CPF_DisableEditOnTemplate)); - property_flags_table.add_pair("CPF_Transient", static_cast>(Unreal::EPropertyFlags::CPF_Transient)); - property_flags_table.add_pair("CPF_Config", static_cast>(Unreal::EPropertyFlags::CPF_Config)); - property_flags_table.add_pair("CPF_DisableEditOnInstance", - static_cast>(Unreal::EPropertyFlags::CPF_DisableEditOnInstance)); - property_flags_table.add_pair("CPF_EditConst", static_cast>(Unreal::EPropertyFlags::CPF_EditConst)); - property_flags_table.add_pair("CPF_GlobalConfig", static_cast>(Unreal::EPropertyFlags::CPF_GlobalConfig)); - property_flags_table.add_pair("CPF_InstancedReference", - static_cast>(Unreal::EPropertyFlags::CPF_InstancedReference)); - property_flags_table.add_pair("CPF_DuplicateTransient", - static_cast>(Unreal::EPropertyFlags::CPF_DuplicateTransient)); - property_flags_table.add_pair("CPF_SaveGame", static_cast>(Unreal::EPropertyFlags::CPF_SaveGame)); - property_flags_table.add_pair("CPF_NoClear", static_cast>(Unreal::EPropertyFlags::CPF_NoClear)); - property_flags_table.add_pair("CPF_ReferenceParm", static_cast>(Unreal::EPropertyFlags::CPF_ReferenceParm)); - property_flags_table.add_pair("CPF_BlueprintAssignable", - static_cast>(Unreal::EPropertyFlags::CPF_BlueprintAssignable)); - property_flags_table.add_pair("CPF_Deprecated", static_cast>(Unreal::EPropertyFlags::CPF_Deprecated)); - property_flags_table.add_pair("CPF_IsPlainOldData", - static_cast>(Unreal::EPropertyFlags::CPF_IsPlainOldData)); - property_flags_table.add_pair("CPF_RepSkip", static_cast>(Unreal::EPropertyFlags::CPF_RepSkip)); - property_flags_table.add_pair("CPF_RepNotify", static_cast>(Unreal::EPropertyFlags::CPF_RepNotify)); - property_flags_table.add_pair("CPF_Interp", static_cast>(Unreal::EPropertyFlags::CPF_Interp)); - property_flags_table.add_pair("CPF_NonTransactional", - static_cast>(Unreal::EPropertyFlags::CPF_NonTransactional)); - property_flags_table.add_pair("CPF_EditorOnly", static_cast>(Unreal::EPropertyFlags::CPF_EditorOnly)); - property_flags_table.add_pair("CPF_NoDestructor", static_cast>(Unreal::EPropertyFlags::CPF_NoDestructor)); - property_flags_table.add_pair("CPF_AutoWeak", static_cast>(Unreal::EPropertyFlags::CPF_AutoWeak)); - property_flags_table.add_pair("CPF_ContainsInstancedReference", - static_cast>(Unreal::EPropertyFlags::CPF_ContainsInstancedReference)); - property_flags_table.add_pair("CPF_AssetRegistrySearchable", - static_cast>(Unreal::EPropertyFlags::CPF_AssetRegistrySearchable)); - property_flags_table.add_pair("CPF_SimpleDisplay", static_cast>(Unreal::EPropertyFlags::CPF_SimpleDisplay)); - property_flags_table.add_pair("CPF_AdvancedDisplay", - static_cast>(Unreal::EPropertyFlags::CPF_AdvancedDisplay)); - property_flags_table.add_pair("CPF_Protected", static_cast>(Unreal::EPropertyFlags::CPF_Protected)); - property_flags_table.add_pair("CPF_BlueprintCallable", - static_cast>(Unreal::EPropertyFlags::CPF_BlueprintCallable)); - property_flags_table.add_pair("CPF_BlueprintAuthorityOnly", - static_cast>(Unreal::EPropertyFlags::CPF_BlueprintAuthorityOnly)); - property_flags_table.add_pair("CPF_TextExportTransient", - static_cast>(Unreal::EPropertyFlags::CPF_TextExportTransient)); - property_flags_table.add_pair("CPF_NonPIEDuplicateTransient", - static_cast>(Unreal::EPropertyFlags::CPF_NonPIEDuplicateTransient)); - property_flags_table.add_pair("CPF_ExposeOnSpawn", static_cast>(Unreal::EPropertyFlags::CPF_ExposeOnSpawn)); - property_flags_table.add_pair("CPF_PersistentInstance", - static_cast>(Unreal::EPropertyFlags::CPF_PersistentInstance)); - property_flags_table.add_pair("CPF_UObjectWrapper", - static_cast>(Unreal::EPropertyFlags::CPF_UObjectWrapper)); - property_flags_table.add_pair("CPF_HasGetValueTypeHash", - static_cast>(Unreal::EPropertyFlags::CPF_HasGetValueTypeHash)); - property_flags_table.add_pair("CPF_NativeAccessSpecifierPublic", - static_cast>(Unreal::EPropertyFlags::CPF_NativeAccessSpecifierPublic)); - property_flags_table.add_pair("CPF_NativeAccessSpecifierProtected", - static_cast>(Unreal::EPropertyFlags::CPF_NativeAccessSpecifierProtected)); - property_flags_table.add_pair("CPF_NativeAccessSpecifierPrivate", - static_cast>(Unreal::EPropertyFlags::CPF_NativeAccessSpecifierPrivate)); - property_flags_table.add_pair("CPF_SkipSerialization", - static_cast>(Unreal::EPropertyFlags::CPF_SkipSerialization)); + ADD_PROPERTY_FLAGS_PAIR(CPF_None) + ADD_PROPERTY_FLAGS_PAIR(CPF_Edit) + ADD_PROPERTY_FLAGS_PAIR(CPF_ConstParm) + ADD_PROPERTY_FLAGS_PAIR(CPF_BlueprintVisible) + ADD_PROPERTY_FLAGS_PAIR(CPF_ExportObject) + ADD_PROPERTY_FLAGS_PAIR(CPF_BlueprintReadOnly) + ADD_PROPERTY_FLAGS_PAIR(CPF_Net) + ADD_PROPERTY_FLAGS_PAIR(CPF_EditFixedSize) + ADD_PROPERTY_FLAGS_PAIR(CPF_Parm) + ADD_PROPERTY_FLAGS_PAIR(CPF_OutParm) + ADD_PROPERTY_FLAGS_PAIR(CPF_ZeroConstructor) + ADD_PROPERTY_FLAGS_PAIR(CPF_ReturnParm) + ADD_PROPERTY_FLAGS_PAIR(CPF_DisableEditOnTemplate) + ADD_PROPERTY_FLAGS_PAIR(CPF_Transient) + ADD_PROPERTY_FLAGS_PAIR(CPF_Config) + ADD_PROPERTY_FLAGS_PAIR(CPF_DisableEditOnInstance) + ADD_PROPERTY_FLAGS_PAIR(CPF_EditConst) + ADD_PROPERTY_FLAGS_PAIR(CPF_GlobalConfig) + ADD_PROPERTY_FLAGS_PAIR(CPF_InstancedReference) + ADD_PROPERTY_FLAGS_PAIR(CPF_DuplicateTransient) + ADD_PROPERTY_FLAGS_PAIR(CPF_SaveGame) + ADD_PROPERTY_FLAGS_PAIR(CPF_NoClear) + ADD_PROPERTY_FLAGS_PAIR(CPF_ReferenceParm) + ADD_PROPERTY_FLAGS_PAIR(CPF_BlueprintAssignable) + ADD_PROPERTY_FLAGS_PAIR(CPF_Deprecated) + ADD_PROPERTY_FLAGS_PAIR(CPF_IsPlainOldData) + ADD_PROPERTY_FLAGS_PAIR(CPF_RepSkip) + ADD_PROPERTY_FLAGS_PAIR(CPF_RepNotify) + ADD_PROPERTY_FLAGS_PAIR(CPF_Interp) + ADD_PROPERTY_FLAGS_PAIR(CPF_NonTransactional) + ADD_PROPERTY_FLAGS_PAIR(CPF_EditorOnly) + ADD_PROPERTY_FLAGS_PAIR(CPF_NoDestructor) + ADD_PROPERTY_FLAGS_PAIR(CPF_AutoWeak) + ADD_PROPERTY_FLAGS_PAIR(CPF_ContainsInstancedReference) + ADD_PROPERTY_FLAGS_PAIR(CPF_AssetRegistrySearchable) + ADD_PROPERTY_FLAGS_PAIR(CPF_SimpleDisplay) + ADD_PROPERTY_FLAGS_PAIR(CPF_AdvancedDisplay) + ADD_PROPERTY_FLAGS_PAIR(CPF_Protected) + ADD_PROPERTY_FLAGS_PAIR(CPF_BlueprintCallable) + ADD_PROPERTY_FLAGS_PAIR(CPF_BlueprintAuthorityOnly) + ADD_PROPERTY_FLAGS_PAIR(CPF_TextExportTransient) + ADD_PROPERTY_FLAGS_PAIR(CPF_NonPIEDuplicateTransient) + ADD_PROPERTY_FLAGS_PAIR(CPF_ExposeOnSpawn) + ADD_PROPERTY_FLAGS_PAIR(CPF_PersistentInstance) + ADD_PROPERTY_FLAGS_PAIR(CPF_UObjectWrapper) + ADD_PROPERTY_FLAGS_PAIR(CPF_HasGetValueTypeHash) + ADD_PROPERTY_FLAGS_PAIR(CPF_NativeAccessSpecifierPublic) + ADD_PROPERTY_FLAGS_PAIR(CPF_NativeAccessSpecifierProtected) + ADD_PROPERTY_FLAGS_PAIR(CPF_NativeAccessSpecifierPrivate) + ADD_PROPERTY_FLAGS_PAIR(CPF_SkipSerialization) property_flags_table.make_global("EPropertyFlags"); +#undef ADD_PROPERTY_FLAGS_PAIR } static auto register_efindname(const LuaMadeSimple::Lua& lua) -> void diff --git a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp index 4f7b3a218..fe9dc8313 100644 --- a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp +++ b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp @@ -156,6 +156,8 @@ namespace RC::LuaMadeSimple Call, Equal, Length, + BinaryAnd, + BinaryOr, }; /** @@ -169,6 +171,8 @@ namespace RC::LuaMadeSimple std::optional call = std::nullopt; std::optional equal = std::nullopt; std::optional length = std::nullopt; + std::optional binary_and = std::nullopt; + std::optional binary_or = std::nullopt; template auto create(MetaMethod metamethod, LuaCallable lua_callable) -> void @@ -192,6 +196,10 @@ namespace RC::LuaMadeSimple return; case MetaMethod::Length: length = lua_callable; + case MetaMethod::BinaryAnd: + binary_and = lua_callable; + case MetaMethod::BinaryOr: + binary_or = lua_callable; } // TODO: use throw_error() here diff --git a/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp b/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp index 411ddfe7d..2fa93aeca 100644 --- a/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp +++ b/deps/first/LuaMadeSimple/src/LuaMadeSimple.cpp @@ -412,6 +412,8 @@ namespace RC::LuaMadeSimple create("__call", metamethods.call); create("__eq", metamethods.equal); create("__len", metamethods.length); + create("__band", metamethods.binary_and); + create("__bor", metamethods.binary_or); return custom_gc_method; }; From 9ef718bf248b0934924ce858f355d67f87c187bf Mon Sep 17 00:00:00 2001 From: UE4SS Date: Fri, 12 Apr 2024 07:30:43 +0200 Subject: [PATCH 12/16] Removed dangerous cast from uint64 to int64. --- .../LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp index fe9dc8313..d6b4ec442 100644 --- a/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp +++ b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp @@ -304,10 +304,6 @@ namespace RC::LuaMadeSimple { lua_pushinteger(get_lua_instance().get_lua_state(), value); } - else if constexpr (std::is_same_v) - { - lua_pushinteger(get_lua_instance().get_lua_state(), static_cast(value)); - } else if constexpr (std::is_same_v) { lua_pushinteger(get_lua_instance().get_lua_state(), static_cast(value)); From 5c896b677cd90878baa6d6a6284592377e4135bf Mon Sep 17 00:00:00 2001 From: Okaetsu <111317036+Okaetsu@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:22:40 +0300 Subject: [PATCH 13/16] Update Changelog --- assets/Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/Changelog.md b/assets/Changelog.md index 447608e19..fa0d4eff2 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -15,6 +15,8 @@ Implemented `NumericProperty`, and its `IsFloatingPoint` member function. Implemented member functions `HasAnyPropertyFlags` and `HasAllPropertyFlags` for Property. +Implemented `RegisterHookFromBP` and `UnregisterHookFromBP` for Blueprint Mods. + ### C++ API ### Experimental From 5f475309f20849fefb6cb7560ee23adf95b1f82d Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 13 Apr 2024 10:00:06 +0200 Subject: [PATCH 14/16] Fixed LuaType::BoolProperty having the wrong metatable This was making LuaType::ObjectProperty use the wrong metatable, and preventing a call to 'GetPropertyClass' from working. --- UE4SS/include/LuaType/LuaXBoolProperty.hpp | 2 +- assets/Changelog.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/UE4SS/include/LuaType/LuaXBoolProperty.hpp b/UE4SS/include/LuaType/LuaXBoolProperty.hpp index 2d4aac18d..3c3d3aade 100644 --- a/UE4SS/include/LuaType/LuaXBoolProperty.hpp +++ b/UE4SS/include/LuaType/LuaXBoolProperty.hpp @@ -13,7 +13,7 @@ namespace RC::LuaType { constexpr static const char* ToString() { - return "ObjectProperty"; + return "BoolProperty"; } }; class XBoolProperty : public RemoteObjectBase diff --git a/assets/Changelog.md b/assets/Changelog.md index fa0d4eff2..a26099c43 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -75,6 +75,8 @@ Fixed the "IterateGameDirectories" global function throwing "bad conversion" err Fixed `FText` not working as a parameter (in `RegisterHook`, etc.) ([UE4SS #422](https://github.com/UE4SS-RE/RE-UE4SS/pull/422)) - localcc +Fixed `BoolProperty` having the same metatable as `ObjectProperty` causing their member functions to occasionally not be found + ### C++ API From f2225d2840cc5e51ef827c7bf1b47cb411894358 Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 13 Apr 2024 10:03:43 +0200 Subject: [PATCH 15/16] Expanded on error message for when OriginalFunction isn't valid Also added an extra check to make sure it's actually valid. Note that there were some formatting errors in this file and this commit also fixes those. --- .../Mods/BPML_GenericFunctions/Scripts/main.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index e65f06179..8dbd5c8a0 100644 --- a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua +++ b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua @@ -87,8 +87,8 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT -- Safeguard for checking if the function hook target is actually valid local OriginalFunction = StaticFindObject(HookName_AsString) - if not OriginalFunction:IsA(FunctionClass) then - error("Not a function!") + if not OriginalFunction:IsValid() or not OriginalFunction:IsA(FunctionClass) then + error(string.format("Function to hook not found: %s", HookName_AsString)) end local FunctionName = FunctionToCall:get():ToString() @@ -112,9 +112,9 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT OriginalFunction:ForEachProperty(function(Property) if Property:HasAnyPropertyFlags( - EPropertyFlags.CPF_ConstParm | - EPropertyFlags.CPF_Parm | - EPropertyFlags.CPF_OutParm | + EPropertyFlags.CPF_ConstParm | + EPropertyFlags.CPF_Parm | + EPropertyFlags.CPF_OutParm | EPropertyFlags.CPF_ReturnParm ) then table.insert(OriginalTypes, Property) @@ -123,9 +123,9 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT CallbackFunction:ForEachProperty(function(Property) if Property:HasAnyPropertyFlags( - EPropertyFlags.CPF_ConstParm | - EPropertyFlags.CPF_Parm | - EPropertyFlags.CPF_OutParm | + EPropertyFlags.CPF_ConstParm | + EPropertyFlags.CPF_Parm | + EPropertyFlags.CPF_OutParm | EPropertyFlags.CPF_ReturnParm ) then table.insert(CallbackTypes, Property) From 056bc0b8f6a926d1f578a05e1c2c1cbe279d9448 Mon Sep 17 00:00:00 2001 From: UE4SS Date: Sat, 13 Apr 2024 10:58:23 +0200 Subject: [PATCH 16/16] Allowing any type of UObject to be the context --- assets/Mods/BPML_GenericFunctions/Scripts/main.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index 8dbd5c8a0..b3d9b6d33 100644 --- a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua +++ b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua @@ -137,10 +137,9 @@ RegisterCustomEvent("RegisterHookFromBP", function (Context, HookName, FunctionT end -- Check that the first callback param matches the expected context class - if not CallbackTypes[1]:IsA(PropertyTypes.ObjectProperty) or CallbackTypes[1]:GetPropertyClass():GetFullName() ~= ExpectedContextClass:GetFullName() then - error(string.format("Expected '%s' as the Context Class, got '%s' instead!", ExpectedContextClass:GetFName():ToString(), - CallbackTypes[1]:GetClass():GetFName():ToString())) - end + if not CallbackTypes[1]:IsA(PropertyTypes.ObjectProperty) then + error(string.format("Context must be an Object of some kind, got '%s' instead!", CallbackTypes[1]:GetClass():GetFName():ToString())) + end -- Remove Context from the table since we're done with it now table.remove(CallbackTypes, 1)