Skip to content

Commit 2ae5db7

Browse files
committed
fix(lua): fix TSet binding issues with type conv. and userdata handling
- Fix array-like table value extraction using insert_value(-2) - Add TSet userdata support in lua_to_memory for round-trip functionality - Fix stack management in for_each_in_table iteration - Resolve string and FName type conversion errors Fixes array interpretation to use values (10,11,12) instead of indices (1,2,3) Enables passing TSet properties as function parameters
1 parent 6df8fa9 commit 2ae5db7

4 files changed

Lines changed: 130 additions & 15 deletions

File tree

UE4SS/src/LuaType/LuaTSet.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,6 @@ namespace RC::LuaType
103103
prepare_to_handle(SetOperation::ForEach, lua);
104104
return 1;
105105
});
106-
107-
// Add size() function for compatibility with other containers
108-
table.add_pair("size",
109-
[](const LuaMadeSimple::Lua& lua) -> int {
110-
auto& lua_object = lua.get_userdata<TSet>();
111-
lua.set_integer(lua_object.get_remote_cpp_object()->Num());
112-
return 1;
113-
});
114106

115107
if constexpr (is_final == LuaMadeSimple::Type::IsFinal::Yes)
116108
{

UE4SS/src/LuaType/LuaUObject.cpp

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,7 @@ namespace RC::LuaType
916916

917917
auto set = new(params.data) Unreal::FScriptSet{};
918918

919+
// Define construct and destruct functions first
919920
auto construct_fn = [&](Unreal::FProperty* property, const void* ptr, void* new_element) {
920921
if (property->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ZeroConstructor))
921922
{
@@ -938,16 +939,19 @@ namespace RC::LuaType
938939
}
939940
};
940941

942+
// The table is at params.stored_at_index
943+
// We need to ensure it's accessible for iteration
944+
941945
params.lua.for_each_in_table([&](LuaMadeSimple::LuaTableReference table) -> bool {
942-
// We don't need to check for key type since we're adding to a set
943-
// and the key doesn't matter for sets
944-
945946
// Prepare temporary storage for the element
946947
Unreal::TArray<Unreal::uint8> element_data{};
947948
element_data.Init(0, info.layout.Size);
948949

949-
// Get the value from Lua into our temporary storage
950-
params.lua.insert_value(-1);
950+
// For sets, we want to use the value, not the key
951+
// The for_each_in_table callback provides key and value
952+
// We need to push the value onto the stack for the pusher
953+
params.lua.insert_value(-2); // Push the value onto the stack
954+
951955
PusherParams pusher_params{.operation = Operation::Set,
952956
.lua = params.lua,
953957
.base = params.base,
@@ -984,6 +988,89 @@ namespace RC::LuaType
984988
});
985989
};
986990

991+
auto lua_to_memory = [&]() {
992+
if (params.lua.is_userdata(params.stored_at_index))
993+
{
994+
// Handle TSet userdata
995+
auto& lua_tset = params.lua.get_userdata<TSet>(params.stored_at_index);
996+
Unreal::FScriptSet* source_set = lua_tset.get_remote_cpp_object();
997+
998+
Unreal::FSetProperty* set_property = static_cast<Unreal::FSetProperty*>(params.property);
999+
FScriptSetInfo info(set_property->GetElementProp());
1000+
1001+
auto set = new(params.data) Unreal::FScriptSet{};
1002+
1003+
// Define construct and destruct functions for copying
1004+
auto construct_fn = [&](Unreal::FProperty* property, const void* ptr, void* new_element) {
1005+
if (property->HasAnyPropertyFlags(Unreal::EPropertyFlags::CPF_ZeroConstructor))
1006+
{
1007+
Unreal::FMemory::Memzero(new_element, property->GetSize());
1008+
}
1009+
else
1010+
{
1011+
property->InitializeValue(new_element);
1012+
}
1013+
1014+
property->CopySingleValueToScriptVM(new_element, ptr);
1015+
};
1016+
1017+
auto destruct_fn = [&](Unreal::FProperty* property, void* element) {
1018+
if (!property->HasAnyPropertyFlags(
1019+
Unreal::EPropertyFlags::CPF_IsPlainOldData |
1020+
Unreal::EPropertyFlags::CPF_NoDestructor))
1021+
{
1022+
property->DestroyValue(element);
1023+
}
1024+
};
1025+
1026+
// Copy elements from source set to destination set
1027+
Unreal::int32 max_index = source_set->GetMaxIndex();
1028+
for (Unreal::int32 i = 0; i < max_index; i++)
1029+
{
1030+
if (!source_set->IsValidIndex(i))
1031+
{
1032+
continue;
1033+
}
1034+
1035+
void* element_ptr = source_set->GetData(i, info.layout);
1036+
1037+
set->Add(element_ptr,
1038+
info.layout,
1039+
[&](const void* src) -> Unreal::uint32 {
1040+
return info.element->GetValueTypeHash(src);
1041+
},
1042+
[&](const void* a, const void* b) -> bool {
1043+
return info.element->Identical(a, b);
1044+
},
1045+
[&](void* new_element) {
1046+
construct_fn(info.element, element_ptr, new_element);
1047+
},
1048+
[&](void* element) {
1049+
destruct_fn(info.element, element);
1050+
});
1051+
}
1052+
1053+
set->Rehash(info.layout,
1054+
[&](const void* src) -> Unreal::uint32 {
1055+
return info.element->GetValueTypeHash(src);
1056+
});
1057+
}
1058+
else if (params.lua.is_table(params.stored_at_index))
1059+
{
1060+
// TSet as table
1061+
lua_table_to_set();
1062+
}
1063+
else if (params.lua.is_nil(params.stored_at_index))
1064+
{
1065+
// Empty set
1066+
new(params.data) Unreal::FScriptSet{};
1067+
}
1068+
else
1069+
{
1070+
params.throw_error("push_setproperty::lua_to_memory", "Parameter must be of type 'TSet' or table");
1071+
}
1072+
};
1073+
9871074
switch (params.operation)
9881075
{
9891076
case Operation::Get:
@@ -993,7 +1080,7 @@ namespace RC::LuaType
9931080
set_to_lua_table(params.lua, params.property, params.data);
9941081
return;
9951082
case Operation::Set:
996-
lua_table_to_set();
1083+
lua_to_memory();
9971084
return;
9981085
case Operation::GetParam:
9991086
RemoteUnrealParam::construct(params.lua, params.data, params.base, params.property);

assets/Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Added ability to call UFunctions directly from the GUI. ([UE4SS #851](https://gi
6363
**Updated Lua version to 5.4.7** ([UE4SS #887](https://github.com/UE4SS-RE/RE-UE4SS/pull/887))
6464
- This is necessary to compile with Clang.
6565

66+
Added `TSet` implementation. [UE4SS #883](https://github.com/UE4SS-RE/RE-UE4SS/pull/883)
67+
6668
Added `TMap` implementation. [UE4SS #755](https://github.com/UE4SS-RE/RE-UE4SS/issues/755)
6769

6870
Added global function `CreateInvalidObject`, which returns an invalid UObject. ([UE4SS #652](https://github.com/UE4SS-RE/RE-UE4SS/issues/652))

assets/Mods/shared/Types.lua

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,41 @@ function TArray:Empty() end
10071007
---@param Callback fun(index: integer, element: RemoteUnrealParam)
10081008
function TArray:ForEach(Callback) end
10091009

1010-
---@class TSet<K> : { [K]: nil }
1010+
---@class TSet<T>
1011+
local TSet = {}
1012+
1013+
---Adds an element to the set
1014+
---@param Element T
1015+
function TSet:Add(Element) end
1016+
1017+
---Checks if the set contains the specified element
1018+
---@param Element T
1019+
---@return boolean
1020+
function TSet:Contains(Element) end
1021+
1022+
---Removes an element from the set
1023+
---@param Element T
1024+
function TSet:Remove(Element) end
1025+
1026+
---Clears all elements from the set
1027+
function TSet:Empty() end
1028+
1029+
---Iterates the entire `TSet` and calls the callback function for each element in the set.
1030+
---The callback has one param: `T element`
1031+
---@param Callback fun(element: T)
1032+
function TSet:ForEach(Callback) end
1033+
1034+
---Returns the number of elements in the set (metamethod for # operator)
1035+
---@return integer
1036+
function TSet:__len() end
1037+
1038+
---Returns whether this TSet is valid
1039+
---@return boolean
1040+
function TSet:IsValid() end
1041+
1042+
---Returns "TSet"
1043+
---@return 'TSet'
1044+
function TSet:type() end
10111045

10121046
---@class TMap<K, V> : { [K]: V }
10131047
local TMap = {}

0 commit comments

Comments
 (0)