Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions UE4SS/generated_include/MacroSetter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ if (auto val = parser.get_int64(STR("UFunction"), STR("EventGraphCallOffset"), -
Unreal::UFunction::MemberOffsets.emplace(STR("EventGraphCallOffset"), static_cast<int32_t>(val));
if (auto val = parser.get_int64(STR("UFunction"), STR("Func"), -1); val != -1)
Unreal::UFunction::MemberOffsets.emplace(STR("Func"), static_cast<int32_t>(val));
if (auto val = parser.get_int64(STR("USparseDelegateFunction"), STR("OwningClassName"), -1); val != -1)
Unreal::USparseDelegateFunction::MemberOffsets.emplace(STR("OwningClassName"), static_cast<int32_t>(val));
if (auto val = parser.get_int64(STR("USparseDelegateFunction"), STR("DelegateName"), -1); val != -1)
Unreal::USparseDelegateFunction::MemberOffsets.emplace(STR("DelegateName"), static_cast<int32_t>(val));
if (auto val = parser.get_int64(STR("UField"), STR("Next"), -1); val != -1)
Unreal::UField::MemberOffsets.emplace(STR("Next"), static_cast<int32_t>(val));
if (auto val = parser.get_int64(STR("FField"), STR("ClassPrivate"), -1); val != -1)
Expand Down
3 changes: 3 additions & 0 deletions UE4SS/include/LuaType/LuaUObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ namespace RC::LuaType
RC_UE4SS_API auto push_strproperty(const PusherParams&) -> void;
RC_UE4SS_API auto push_softobjectproperty(const PusherParams&) -> void;
RC_UE4SS_API auto push_interfaceproperty(const PusherParams&) -> void;
RC_UE4SS_API auto push_delegateproperty(const PusherParams&) -> void;
RC_UE4SS_API auto push_multicastdelegateproperty(const PusherParams&) -> void;
RC_UE4SS_API auto push_multicastsparsedelegateproperty(const PusherParams&) -> void;

RC_UE4SS_API auto push_functionproperty(const FunctionPusherParams&) -> void;
// Push to Lua -> END
Expand Down
117 changes: 117 additions & 0 deletions UE4SS/include/LuaType/LuaXDelegateProperty.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include <LuaType/LuaXProperty.hpp>

namespace RC::Unreal
{
class FDelegateProperty;
class FMulticastDelegateProperty;
class FMulticastSparseDelegateProperty;
}

namespace RC::LuaType
{
struct PusherParams;

// FDelegateProperty (single-cast delegate)
struct FDelegatePropertyName
{
constexpr static const char* ToString()
{
return "DelegateProperty";
}
};

class XDelegateProperty : public RemoteObjectBase<Unreal::FDelegateProperty, FDelegatePropertyName>
{
public:
using Super = XProperty;

private:
explicit XDelegateProperty(Unreal::FDelegateProperty* object);

public:
XDelegateProperty() = delete;
auto static construct(const LuaMadeSimple::Lua&, Unreal::FDelegateProperty*) -> const LuaMadeSimple::Lua::Table;
auto static construct(const LuaMadeSimple::Lua&, BaseObject&) -> const LuaMadeSimple::Lua::Table;

private:
auto static setup_metamethods(BaseObject&) -> void;

private:
template <LuaMadeSimple::Type::IsFinal is_final>
auto static setup_member_functions(const LuaMadeSimple::Lua::Table&) -> void;
};

// FMulticastDelegateProperty (multicast inline delegate)
struct FMulticastDelegatePropertyName
{
constexpr static const char* ToString()
{
return "MulticastDelegateProperty";
}
};

class XMulticastDelegateProperty : public RemoteObjectBase<Unreal::FMulticastDelegateProperty, FMulticastDelegatePropertyName>
{
public:
using Super = XProperty;

private:
Unreal::UObject* m_base{};
Unreal::FMulticastDelegateProperty* m_property{};

private:
explicit XMulticastDelegateProperty(Unreal::FMulticastDelegateProperty* object);
explicit XMulticastDelegateProperty(const PusherParams&);

public:
XMulticastDelegateProperty() = delete;
auto static construct(const PusherParams&) -> const LuaMadeSimple::Lua::Table;
auto static construct(const LuaMadeSimple::Lua&, Unreal::FMulticastDelegateProperty*) -> const LuaMadeSimple::Lua::Table;
auto static construct(const LuaMadeSimple::Lua&, BaseObject&) -> const LuaMadeSimple::Lua::Table;

private:
auto static setup_metamethods(BaseObject&) -> void;

private:
template <LuaMadeSimple::Type::IsFinal is_final>
auto static setup_member_functions(const LuaMadeSimple::Lua::Table&) -> void;
};

// FMulticastSparseDelegateProperty (multicast sparse delegate)
struct FMulticastSparseDelegatePropertyName
{
constexpr static const char* ToString()
{
return "MulticastSparseDelegateProperty";
}
};

class XMulticastSparseDelegateProperty : public RemoteObjectBase<Unreal::FMulticastSparseDelegateProperty, FMulticastSparseDelegatePropertyName>
{
public:
using Super = XProperty;

private:
Unreal::UObject* m_base{};
Unreal::FMulticastSparseDelegateProperty* m_property{};

private:
explicit XMulticastSparseDelegateProperty(Unreal::FMulticastSparseDelegateProperty* object);
explicit XMulticastSparseDelegateProperty(const PusherParams&);

public:
XMulticastSparseDelegateProperty() = delete;
auto static construct(const PusherParams&) -> const LuaMadeSimple::Lua::Table;
auto static construct(const LuaMadeSimple::Lua&, Unreal::FMulticastSparseDelegateProperty*) -> const LuaMadeSimple::Lua::Table;
auto static construct(const LuaMadeSimple::Lua&, BaseObject&) -> const LuaMadeSimple::Lua::Table;

private:
auto static setup_metamethods(BaseObject&) -> void;

private:
template <LuaMadeSimple::Type::IsFinal is_final>
auto static setup_member_functions(const LuaMadeSimple::Lua::Table&) -> void;
};
} // namespace RC::LuaType
218 changes: 218 additions & 0 deletions UE4SS/src/LuaType/LuaUObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <LuaType/LuaUStruct.hpp>
#include <LuaType/LuaUWorld.hpp>
#include <LuaType/LuaUDataTable.hpp>
#include <LuaType/LuaXDelegateProperty.hpp>
#include <LuaType/LuaXObjectProperty.hpp>
#include <LuaType/LuaXProperty.hpp>
#pragma warning(disable : 4005)
Expand All @@ -33,6 +34,9 @@
#include <Unreal/Property/FSoftClassProperty.hpp>
#include <Unreal/Property/FStructProperty.hpp>
#include <Unreal/Property/FWeakObjectProperty.hpp>
#include <Unreal/Property/FDelegateProperty.hpp>
#include <Unreal/Property/FMulticastDelegateProperty.hpp>
#include <Unreal/Property/FMulticastSparseDelegateProperty.hpp>
#include <Unreal/Property/NumericPropertyTypes.hpp>
#include <Unreal/UClass.hpp>
#include <Unreal/UEnum.hpp>
Expand Down Expand Up @@ -1738,6 +1742,220 @@ namespace RC::LuaType
}
}

auto push_delegateproperty(const PusherParams& params) -> void
{
using namespace Unreal;
auto* delegate_value = static_cast<FScriptDelegate*>(params.data);
if (!delegate_value)
{
params.throw_error("push_delegateproperty", "data pointer is nullptr");
}

switch (params.operation)
{
case Operation::GetNonTrivialLocal:
case Operation::Get: {
// Return a table with {Object = UObject, FunctionName = FName}
LuaMadeSimple::Lua::Table lua_table = params.lua.prepare_new_table();

lua_table.add_key("Object");
auto_construct_object(params.lua, delegate_value->GetUObject());
lua_table.fuse_pair();

lua_table.add_key("FunctionName");
FName::construct(params.lua, delegate_value->GetFunctionName());
lua_table.fuse_pair();

lua_table.make_local();
break;
}
case Operation::Set: {
if (params.lua.is_table())
{
lua_pushstring(params.lua.get_lua_state(), "Object");
int adjusted_index = params.stored_at_index;
if (params.stored_at_index < 0)
{
adjusted_index = params.stored_at_index - 1;
}
lua_rawget(params.lua.get_lua_state(), adjusted_index);

Unreal::UObject* obj = nullptr;
if (params.lua.is_userdata())
{
const auto& lua_object = params.lua.get_userdata<LuaType::UObject>();
obj = lua_object.get_remote_cpp_object();
}
params.lua.discard_value();

lua_pushstring(params.lua.get_lua_state(), "FunctionName");
lua_rawget(params.lua.get_lua_state(), adjusted_index);

Unreal::FName fname = NAME_None;
if (params.lua.is_userdata())
{
auto& lua_fname = params.lua.get_userdata<LuaType::FName>();
fname = lua_fname.get_local_cpp_object();
}
params.lua.discard_value();

// Bind the delegate - this modifies the existing FWeakObjectPtr which calls
// the engine's assignment operator that will allocate serial numbers
delegate_value->BindUFunction(obj, fname);
params.lua.discard_value(params.stored_at_index);
}
else if (params.lua.is_nil())
{
delegate_value->Clear();
params.lua.discard_value(params.stored_at_index);
}
else
{
params.throw_error("push_delegateproperty", "Value must be a table {Object = UObject, FunctionName = FName} or nil");
}
break;
}
case Operation::GetParam:
RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property);
break;
default:
params.throw_error("push_delegateproperty", "Operation not supported");
break;
}
}

auto push_multicastdelegateproperty(const PusherParams& params) -> void
{
using namespace Unreal;

auto* delegate_property = static_cast<FMulticastDelegateProperty*>(params.property);
if (!delegate_property)
{
params.throw_error("push_multicastdelegateproperty", "property is not FMulticastDelegateProperty");
}

switch (params.operation)
{
case Operation::Get:
XMulticastDelegateProperty::construct(params);
break;
case Operation::GetNonTrivialLocal: {
const FMulticastScriptDelegate* delegate_value = delegate_property->GetMulticastDelegate(params.data);
if (!delegate_value)
{
params.lua.set_nil();
break;
}

LuaMadeSimple::Lua::Table lua_table = params.lua.prepare_new_table();

int32 count = delegate_value->Num();
for (int32 i = 0; i < count; ++i)
{
lua_table.add_key(i + 1); // Lua is 1-indexed

LuaMadeSimple::Lua::Table delegate_entry = params.lua.prepare_new_table();

delegate_entry.add_key("Object");
auto_construct_object(params.lua, delegate_value->InvocationList[i].GetUObject());
delegate_entry.fuse_pair();

delegate_entry.add_key("FunctionName");
FName::construct(params.lua, delegate_value->InvocationList[i].GetFunctionName());
delegate_entry.fuse_pair();

delegate_entry.make_local();
lua_table.fuse_pair(); // Set array element
}

lua_table.make_local();
break;
}
case Operation::Set:
// Multicast delegates cannot be set directly. Use the property wrapper methods instead:
// local prop = obj.OnSomething
// prop:Add(targetObj, FName("FunctionName"))
// prop:Remove(targetObj, FName("FunctionName"))
// prop:Clear()
params.throw_error("push_multicastdelegateproperty",
"Multicast delegate values are read-only. Use property methods: GetPropertyByName():Add/Remove/Clear");
break;
case Operation::GetParam:
RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property);
break;
default:
params.throw_error("push_multicastdelegateproperty", "Operation not supported");
break;
}
}

auto push_multicastsparsedelegateproperty(const PusherParams& params) -> void
{
using namespace Unreal;

// Sparse delegates work similarly to regular multicast delegates
// The main difference is they use 1 byte + global storage instead of inline InvocationList
auto* delegate_property = static_cast<FMulticastSparseDelegateProperty*>(params.property);
if (!delegate_property)
{
params.throw_error("push_multicastsparsedelegateproperty", "property is not FMulticastSparseDelegateProperty");
}

switch (params.operation)
{
case Operation::Get:
XMulticastSparseDelegateProperty::construct(params);
break;
case Operation::GetNonTrivialLocal: {
const FMulticastScriptDelegate* delegate_value = delegate_property->GetMulticastDelegate(params.data);
if (!delegate_value)
{
params.lua.set_nil();
break;
}

LuaMadeSimple::Lua::Table lua_table = params.lua.prepare_new_table();

int32 count = delegate_value->Num();
for (int32 i = 0; i < count; ++i)
{
lua_table.add_key(i + 1); // Lua is 1-indexed

LuaMadeSimple::Lua::Table delegate_entry = params.lua.prepare_new_table();

delegate_entry.add_key("Object");
auto_construct_object(params.lua, delegate_value->InvocationList[i].GetUObject());
delegate_entry.fuse_pair();

delegate_entry.add_key("FunctionName");
FName::construct(params.lua, delegate_value->InvocationList[i].GetFunctionName());
delegate_entry.fuse_pair();

delegate_entry.make_local();
lua_table.fuse_pair();
}

lua_table.make_local();
break;
}
case Operation::Set:
// Multicast delegates cannot be set directly. Use the property wrapper methods instead:
// local prop = obj.OnSomething
// prop:Add(targetObj, FName("FunctionName"))
// prop:Remove(targetObj, FName("FunctionName"))
// prop:Clear()
params.throw_error("push_multicastsparsedelegateproperty",
"Sparse multicast delegate values are read-only. Use property methods: GetPropertyByName():Add/Remove/Clear");
break;
case Operation::GetParam:
RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property);
break;
default:
params.throw_error("push_multicastsparsedelegateproperty", "Operation not supported");
break;
}
}

auto static is_a_internal(const LuaMadeSimple::Lua& lua, Unreal::UObject* object, Unreal::UClass* object_class) -> bool
{
if (!object || !object_class)
Expand Down
Loading