6
6
#include " unrealsdk/format.h"
7
7
#include " unrealsdk/unreal/classes/uclass.h"
8
8
#include " unrealsdk/unreal/classes/uobject.h"
9
+ #include " unrealsdk/unreal/classes/uproperty.h"
10
+ #include " unrealsdk/unreal/find_class.h"
9
11
#include " unrealsdk/unreal/structs/fname.h"
10
12
#include " unrealsdk/unrealsdk.h"
11
13
#include " unrealsdk/utils.h"
@@ -29,6 +31,11 @@ UObject* uobject_init(const py::args& /* args */, const py::kwargs& /* kwargs */
29
31
throw py::type_error (" Cannot create new instances of unreal objects." );
30
32
}
31
33
34
+ // Dummy class to make the context manager on
35
+ struct ContextManager {};
36
+
37
+ size_t should_notify_counter = 0 ;
38
+
32
39
} // namespace
33
40
34
41
void register_uobject (py::module_& mod) {
@@ -126,8 +133,12 @@ void register_uobject(py::module_& mod) {
126
133
}
127
134
}
128
135
129
- py_setattr_direct (py_find_field (py::cast<FName>(name), self->Class ),
130
- reinterpret_cast <uintptr_t >(self), value);
136
+ auto field = py_find_field (py::cast<FName>(name), self->Class );
137
+ py_setattr_direct (field, reinterpret_cast <uintptr_t >(self), value);
138
+
139
+ if (should_notify_counter > 0 && field->is_instance (find_class<UProperty>())) {
140
+ self->post_edit_change_property (reinterpret_cast <UProperty*>(field));
141
+ }
131
142
},
132
143
" Writes a value to an unreal field on the object.\n "
133
144
" \n "
@@ -144,6 +155,10 @@ void register_uobject(py::module_& mod) {
144
155
throw py::attribute_error (" cannot access null attribute" );
145
156
}
146
157
py_setattr_direct (field, reinterpret_cast <uintptr_t >(self), value);
158
+
159
+ if (should_notify_counter > 0 && field->is_instance (find_class<UProperty>())) {
160
+ self->post_edit_change_property (reinterpret_cast <UProperty*>(field));
161
+ }
147
162
},
148
163
" Writes a value to an unreal field on the object.\n "
149
164
" \n "
@@ -162,11 +177,68 @@ void register_uobject(py::module_& mod) {
162
177
" \n "
163
178
" Returns:\n "
164
179
" This object's address." )
180
+ .def (
181
+ " _post_edit_change_property" ,
182
+ [](UObject* self, std::variant<FName, UProperty*> prop) {
183
+ std::visit ([self](auto && val) { self->post_edit_change_property (val); }, prop);
184
+ },
185
+ " Notifies the engine that we've made an external change to a property.\n "
186
+ " \n "
187
+ " This only works on top level properties, those directly on the object.\n "
188
+ " \n "
189
+ " Also see the notify_changes() context manager, which calls this automatically.\n "
190
+ " \n "
191
+ " Args:\n "
192
+ " prop: The property, or the name of the property, which was changed." ,
193
+ " args" _a)
194
+ .def (
195
+ " _post_edit_change_chain_property" ,
196
+ [](UObject* self, UProperty* prop, const py::args& args) {
197
+ std::vector<UProperty*> chain;
198
+ chain.reserve (args.size ());
199
+
200
+ for (const auto & val : args) {
201
+ chain.push_back (py::cast<UProperty*>(val));
202
+ }
203
+ self->post_edit_change_chain_property (prop, chain);
204
+ },
205
+ " Notifies the engine that we've made an external change to a chain of properties.\n "
206
+ " \n "
207
+ " This version allows notifying about changes inside (nested) structs.\n "
208
+ " \n "
209
+ " Args:\n "
210
+ " prop: The property which was changed.\n "
211
+ " *chain: The chain of properties to follow." ,
212
+ " prop" _a)
165
213
.def_readwrite (" ObjectFlags" , &UObject::ObjectFlags)
166
214
.def_readwrite (" InternalIndex" , &UObject::InternalIndex)
167
215
.def_readwrite (" Class" , &UObject::Class)
168
216
.def_readwrite (" Name" , &UObject::Name)
169
217
.def_readwrite (" Outer" , &UObject::Outer);
218
+
219
+ // Create under an empty handle to prevent this type being normally accessible
220
+ py::class_<ContextManager>(py::handle (), " context_manager" , pybind11::module_local ())
221
+ .def (" __enter__" , [](const py::object& /* self*/ ) { should_notify_counter++; })
222
+ .def (" __exit__" , [](const py::object& /* self */ , const py::object& /* exc_type*/ ,
223
+ const py::object& /* exc_value*/ , const py::object& /* traceback*/ ) {
224
+ if (should_notify_counter > 0 ) {
225
+ should_notify_counter--;
226
+ }
227
+ });
228
+
229
+ mod.def (
230
+ " notify_changes" , []() { return ContextManager{}; },
231
+ " Context manager to automatically notify the engine when you edit an object.\n "
232
+ " \n "
233
+ " This essentially just automatically calls obj._post_edit_change_property() after\n "
234
+ " every setattr.\n "
235
+ " \n "
236
+ " Note that this only tracks top-level changes, it cannot track changes to inner\n "
237
+ " struct fields, You will have to manually call obj._post_edit_chain_property()\n "
238
+ " for them.\n "
239
+ " \n "
240
+ " Returns:\n "
241
+ " A new context manager." );
170
242
}
171
243
172
244
} // namespace pyunrealsdk::unreal
0 commit comments