1+ #include < LuaType/LuaTSet.hpp>
2+
3+ #include < DynamicOutput/DynamicOutput.hpp>
4+
5+ #include < Unreal/Property/FSetProperty.hpp>
6+
7+ namespace RC ::LuaType
8+ {
9+ TSet::TSet (const PusherParams& params)
10+ : RemoteObjectBase<Unreal::FScriptSet, TSetName>(static_cast <Unreal::FScriptSet*>(params.data)), m_base(params.base),
11+ m_property (static_cast <Unreal::FSetProperty*>(params.property)), m_element_property(m_property->GetElementProp ())
12+ {
13+ }
14+
15+ auto TSet::construct (const PusherParams& params) -> const LuaMadeSimple::Lua::Table
16+ {
17+ LuaType::TSet lua_object{params};
18+
19+ if (!lua_object.m_element_property )
20+ {
21+ Output::send<LogLevel::Error>(STR (" TSet::construct: m_element_property is nullptr for {}" ), lua_object.m_property ->GetFullName ());
22+ }
23+
24+ auto metatable_name = ClassName::ToString ();
25+
26+ LuaMadeSimple::Lua::Table table = params.lua .get_metatable (metatable_name);
27+ if (params.lua .is_nil (-1 ))
28+ {
29+ params.lua .discard_value (-1 );
30+ LuaMadeSimple::Type::RemoteObject<Unreal::FScriptSet>::construct (params.lua , lua_object);
31+ setup_metamethods (lua_object);
32+ setup_member_functions<LuaMadeSimple::Type::IsFinal::Yes>(table);
33+ params.lua .new_metatable <LuaType::TSet>(metatable_name, lua_object.get_metamethods ());
34+ }
35+
36+ // Create object & surrender ownership to lua
37+ params.lua .transfer_stack_object (std::move (lua_object), metatable_name, lua_object.get_metamethods ());
38+
39+ return table;
40+ }
41+
42+ auto TSet::construct (const LuaMadeSimple::Lua& lua, BaseObject& construct_to) -> const LuaMadeSimple::Lua::Table
43+ {
44+ LuaMadeSimple::Lua::Table table = LuaMadeSimple::Type::RemoteObject<Unreal::FScriptSet>::construct (lua, construct_to);
45+
46+ setup_member_functions<LuaMadeSimple::Type::IsFinal::No>(table);
47+
48+ setup_metamethods (construct_to);
49+
50+ return table;
51+ }
52+
53+ auto TSet::setup_metamethods (BaseObject& base_object) -> void
54+ {
55+ // Add Length metamethod (# operator in Lua)
56+ base_object.get_metamethods ().create (LuaMadeSimple::Lua::MetaMethod::Length,
57+ [](const LuaMadeSimple::Lua& lua) {
58+ auto lua_object = lua.get_userdata <TSet>();
59+ lua.set_integer (lua_object.get_remote_cpp_object ()->Num ());
60+ return 1 ;
61+ });
62+
63+ }
64+
65+ template <LuaMadeSimple::Type::IsFinal is_final>
66+ auto TSet::setup_member_functions (const LuaMadeSimple::Lua::Table& table) -> void
67+ {
68+ table.add_pair (" IsValid" ,
69+ [](const LuaMadeSimple::Lua& lua) -> int {
70+ auto & lua_object = lua.get_userdata <TSet>();
71+
72+ lua.set_bool (lua_object.get_remote_cpp_object ());
73+
74+ return 1 ;
75+ });
76+
77+ table.add_pair (" Add" ,
78+ [](const LuaMadeSimple::Lua& lua) -> int {
79+ prepare_to_handle (SetOperation::Add, lua);
80+ return 1 ;
81+ });
82+
83+ table.add_pair (" Contains" ,
84+ [](const LuaMadeSimple::Lua& lua) -> int {
85+ prepare_to_handle (SetOperation::Contains, lua);
86+ return 1 ;
87+ });
88+
89+ table.add_pair (" Remove" ,
90+ [](const LuaMadeSimple::Lua& lua) -> int {
91+ prepare_to_handle (SetOperation::Remove, lua);
92+ return 1 ;
93+ });
94+
95+ table.add_pair (" Empty" ,
96+ [](const LuaMadeSimple::Lua& lua) -> int {
97+ prepare_to_handle (SetOperation::Empty, lua);
98+ return 1 ;
99+ });
100+
101+ table.add_pair (" ForEach" ,
102+ [](const LuaMadeSimple::Lua& lua) -> int {
103+ prepare_to_handle (SetOperation::ForEach, lua);
104+ return 1 ;
105+ });
106+
107+ if constexpr (is_final == LuaMadeSimple::Type::IsFinal::Yes)
108+ {
109+ table.add_pair (" type" ,
110+ [](const LuaMadeSimple::Lua& lua) -> int {
111+ lua.set_string (ClassName::ToString ());
112+ return 1 ;
113+ });
114+ }
115+ }
116+
117+ auto TSet::prepare_to_handle (const SetOperation operation, const LuaMadeSimple::Lua& lua) -> void
118+ {
119+ TSet& lua_object = lua.get_userdata <TSet>();
120+
121+ FScriptSetInfo info (lua_object.m_element_property );
122+ info.validate_pushers (lua);
123+
124+ Unreal::FScriptSet* set = lua_object.get_remote_cpp_object ();
125+
126+ switch (operation)
127+ {
128+ case SetOperation::Add: {
129+ Unreal::TArray<Unreal::uint8> element_data{};
130+ element_data.Init (0 , info.layout .Size );
131+
132+ PusherParams pusher_params{.operation = LuaMadeSimple::Type::Operation::Set,
133+ .lua = lua,
134+ .base = lua_object.m_base ,
135+ .data = element_data.GetData (),
136+ .property = info.element };
137+ StaticState::m_property_value_pushers[static_cast <int32_t >(info.element_fname .GetComparisonIndex ())](pusher_params);
138+
139+ void * element_ptr = element_data.GetData ();
140+
141+ auto construct_fn = [&](Unreal::FProperty* property, const void * ptr, void * new_element) {
142+ if (property->HasAnyPropertyFlags (Unreal::EPropertyFlags::CPF_ZeroConstructor))
143+ {
144+ Unreal::FMemory::Memzero (new_element, property->GetSize ());
145+ }
146+ else
147+ {
148+ property->InitializeValue (new_element);
149+ }
150+
151+ property->CopySingleValueToScriptVM (new_element, ptr);
152+ };
153+
154+ auto destruct_fn = [&](Unreal::FProperty* property, void * element) {
155+ if (!property->HasAnyPropertyFlags (
156+ Unreal::EPropertyFlags::CPF_IsPlainOldData |
157+ Unreal::EPropertyFlags::CPF_NoDestructor))
158+ {
159+ property->DestroyValue (element);
160+ }
161+ };
162+
163+ set->Add (element_ptr,
164+ info.layout ,
165+ [&](const void * src) -> Unreal::uint32 {
166+ return info.element ->GetValueTypeHash (src);
167+ },
168+ [&](const void * a, const void * b) -> bool {
169+ return info.element ->Identical (a, b);
170+ },
171+ [&](void * new_element) {
172+ construct_fn (info.element , element_ptr, new_element);
173+ },
174+ [&](void * element) {
175+ destruct_fn (info.element , element);
176+ });
177+ break ;
178+ }
179+ case SetOperation::Contains: {
180+ Unreal::TArray<Unreal::uint8> element_data{};
181+ element_data.Init (0 , info.layout .Size );
182+
183+ PusherParams pusher_params{.operation = LuaMadeSimple::Type::Operation::Set,
184+ .lua = lua,
185+ .base = lua_object.m_base ,
186+ .data = element_data.GetData (),
187+ .property = info.element };
188+ StaticState::m_property_value_pushers[static_cast <int32_t >(info.element_fname .GetComparisonIndex ())](pusher_params);
189+
190+ // Create a SetHelper with the set data
191+ Unreal::FScriptSetHelper SetHelper (lua_object.m_property , set);
192+
193+ // Use FindElementIndex with just the element data
194+ Unreal::int32 index = SetHelper.FindElementIndex (element_data.GetData ());
195+
196+ lua.set_bool (index != Unreal::INDEX_NONE);
197+
198+ break ;
199+ }
200+ case SetOperation::Remove: {
201+ Unreal::TArray<Unreal::uint8> element_data{};
202+ element_data.Init (0 , info.layout .Size );
203+
204+ PusherParams pusher_params{.operation = LuaMadeSimple::Type::Operation::Set,
205+ .lua = lua,
206+ .base = lua_object.m_base ,
207+ .data = element_data.GetData (),
208+ .property = info.element };
209+ StaticState::m_property_value_pushers[static_cast <int32_t >(info.element_fname .GetComparisonIndex ())](pusher_params);
210+
211+ // Create a SetHelper with the set data
212+ Unreal::FScriptSetHelper SetHelper (lua_object.m_property , set);
213+
214+ // Use FindElementIndex with just the element data
215+ Unreal::int32 index = SetHelper.FindElementIndex (element_data.GetData ());
216+
217+ if (index != Unreal::INDEX_NONE)
218+ {
219+ SetHelper.RemoveAt (index);
220+ }
221+
222+ break ;
223+ }
224+ case SetOperation::Empty: {
225+ set->Empty (0 , info.layout );
226+ break ;
227+ }
228+ case SetOperation::ForEach: {
229+ Unreal::int32 max_index = set->GetMaxIndex ();
230+ for (Unreal::int32 i = 0 ; i < max_index; i++)
231+ {
232+ if (!set->IsValidIndex (i))
233+ {
234+ continue ;
235+ }
236+
237+ // Duplicate the Lua function so we can use it in subsequent iterations
238+ lua_pushvalue (lua.get_lua_state (), 1 );
239+
240+ void * element_data = set->GetData (i, info.layout );
241+
242+ // pass element (P1)
243+ PusherParams pusher_params{.operation = LuaMadeSimple::Type::Operation::GetParam,
244+ .lua = lua,
245+ .base = lua_object.m_base ,
246+ .data = element_data,
247+ .property = info.element };
248+
249+ StaticState::m_property_value_pushers[static_cast <int32_t >(info.element_fname .GetComparisonIndex ())](pusher_params);
250+
251+ // Call function passing element
252+ lua.call_function (1 , 0 );
253+ }
254+ break ;
255+ }
256+ }
257+ }
258+
259+ FScriptSetInfo::FScriptSetInfo (Unreal::FProperty* element) :
260+ element (element),
261+ element_fname (element->GetClass ().GetFName())
262+ {
263+ layout = Unreal::FScriptSet::GetScriptLayout (element->GetSize (),
264+ element->GetMinAlignment ());
265+ }
266+
267+ void FScriptSetInfo::validate_pushers (const LuaMadeSimple::Lua& lua)
268+ {
269+ int32_t element_comparison_index = static_cast <int32_t >(element_fname.GetComparisonIndex ());
270+ if (!StaticState::m_property_value_pushers.contains (element_comparison_index))
271+ {
272+ std::string element_type_name = to_string (element_fname.ToString ());
273+ lua.throw_error (fmt::format (" Tried interacting with a set with an unsupported element type {}" , element_type_name));
274+ }
275+ }
276+ }
0 commit comments