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/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/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/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/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()); 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/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 dc9944626..ca3e314fe 100644 --- a/UE4SS/src/LuaType/LuaXProperty.cpp +++ b/UE4SS/src/LuaType/LuaXProperty.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #pragma warning(disable : 4005) #include #include @@ -14,6 +16,7 @@ #include #include #include +#include #pragma warning(default : 4005) namespace RC::LuaType @@ -45,6 +48,10 @@ namespace RC::LuaType { XArrayProperty::construct(lua, as_array_property); } + else if (auto* as_numeric_property = Unreal::CastField(property); as_numeric_property) + { + XNumericProperty::construct(lua, as_numeric_property); + } else { XProperty::construct(lua, property); @@ -137,6 +144,44 @@ 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_userdata()) + { + lua.throw_error(error_overload_not_found); + } + + 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; + }); + + 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_userdata()) + { + lua.throw_error(error_overload_not_found); + } + + 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; + }); + table.add_pair("IsA", [](const LuaMadeSimple::Lua& lua) -> int { std::string error_overload_not_found{R"( No overload found for function 'IsA'. @@ -178,6 +223,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); diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index e26abbf74..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) @@ -617,6 +618,68 @@ namespace RC object_internal_flags_table.make_global("EInternalObjectFlags"); } + 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(); + 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 { LuaMadeSimple::Lua::Table efindname_table = lua.prepare_new_table(); @@ -3300,6 +3363,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 +3513,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/assets/Changelog.md b/assets/Changelog.md index e0a8cac07..a26099c43 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -11,6 +11,11 @@ TBD ### UHT Dumper ### Lua API +Implemented `NumericProperty`, and its `IsFloatingPoint` member function. + +Implemented member functions `HasAnyPropertyFlags` and `HasAllPropertyFlags` for Property. + +Implemented `RegisterHookFromBP` and `UnregisterHookFromBP` for Blueprint Mods. ### C++ API @@ -70,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 diff --git a/assets/Mods/BPML_GenericFunctions/Scripts/main.lua b/assets/Mods/BPML_GenericFunctions/Scripts/main.lua index 7e8017d43..b3d9b6d33 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) @@ -65,3 +72,164 @@ 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() + + -- Safeguard for checking if the function hook target is actually valid + local OriginalFunction = StaticFindObject(HookName_AsString) + 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() + local CallbackFunction = Context:get()[FunctionName] + if not CallbackFunction:IsA(FunctionClass) 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 #CallbackTypes == 0 then + error("Callback function must have atleast one param for Context!") + end + + -- Check that the first callback param matches the expected context class + 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) + + 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 + + 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 = CallbackFunction, + 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) 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/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp b/deps/first/LuaMadeSimple/include/LuaMadeSimple/LuaMadeSimple.hpp index a4ca10f2f..d6b4ec442 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; }; 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..58ac64398 --- /dev/null +++ b/docs/lua-api/classes/numericproperty.md @@ -0,0 +1,11 @@ +# NumericProperty + +## Inheritance +[Property](./property.md) + +## Methods + +### IsFloatingPoint() + +- **Return type:** `bool` +- **Returns:**` Whether this NumericProperty represents a floating point number. \ No newline at end of file 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