2626
2727DEFINE_LOG_CATEGORY (LogPython);
2828
29+
30+
31+
32+
33+
2934PyDoc_STRVAR (unreal_engine_py_doc, " Unreal Engine Python module." );
3035
3136static PyModuleDef unreal_engine_module = {
@@ -36,8 +41,12 @@ static PyModuleDef unreal_engine_module = {
3641 NULL ,
3742};
3843
44+ UPythonGCManager *PythonGCManager;
3945
4046static PyObject *init_unreal_engine (void ) {
47+
48+
49+
4150 return PyModule_Create (&unreal_engine_module);
4251}
4352
@@ -254,28 +263,15 @@ static PyMethodDef ue_PyUObject_methods[] = {
254263};
255264
256265
257-
258- static void ue_pyobject_clear (ue_PyUObject *self) {
259- Py_ssize_t len = PyList_Size (self->python_delegates_gc );
260- for (Py_ssize_t i = 0 ; i < len; i++) {
261- PyObject *item = PyList_GetItem (self->python_delegates_gc , i);
262- UObject *py_delegate = (UObject *)PyLong_AsLongLong (item);
263- // avoid making mess on engine shutdown
264- if (!py_delegate || !py_delegate->IsValidLowLevel () || py_delegate->IsPendingKillOrUnreachable ())
265- continue ;
266- if (py_delegate->IsRooted ())
267- py_delegate->RemoveFromRoot ();
268- py_delegate->ConditionalBeginDestroy ();
269- }
270- #if UEPY_MEMORY_DEBUG
271- UE_LOG (LogPython, Warning, TEXT (" destroying pyobject with %d over %p" ), self->python_delegates_gc ->ob_refcnt , self->ue_property );
272- #endif
273- Py_DECREF (self->python_delegates_gc );
274- }
275-
276266// destructor
277267static void ue_pyobject_dealloc (ue_PyUObject *self) {
278- ue_pyobject_clear (self);
268+ #if UEPY_MEMORY_DEBUG
269+ UE_LOG (LogPython, Warning, TEXT (" Destroying ue_PyUObject %p mapped to UObject %p" ), self, self->ue_object );
270+ #endif
271+ // this could happen during engine shutdown, so have mercy
272+ if (PythonGCManager && PythonGCManager->IsValidLowLevel ()) {
273+ PythonGCManager->python_properties_gc .Remove (self->ue_property );
274+ }
279275 Py_TYPE (self)->tp_free ((PyObject *)self);
280276}
281277
@@ -371,6 +367,11 @@ void unreal_engine_init_py_module() {
371367// utility functions
372368
373369ue_PyUObject *ue_get_python_wrapper (UObject *ue_obj) {
370+ if (!PythonGCManager) {
371+ PythonGCManager = NewObject<UPythonGCManager>();
372+ PythonGCManager->AddToRoot ();
373+ UE_LOG (LogPython, Log, TEXT (" Initialized Python GC Manager" ));
374+ }
374375 if (!ue_obj || !ue_obj->IsValidLowLevel () || ue_obj->IsPendingKillOrUnreachable ())
375376 return nullptr ;
376377 UPyObject *ue_py_object = FindObject<UPyObject>(ue_obj, TEXT (" __PyObject" ), true );
@@ -385,16 +386,21 @@ ue_PyUObject *ue_get_python_wrapper(UObject *ue_obj) {
385386 return nullptr ;
386387 }
387388 ((ue_PyUObject *)ue_py_object->py_object )->ue_object = ue_obj;
388- ((ue_PyUObject *)ue_py_object->py_object )->python_delegates_gc = PyList_New (0 );
389389 ((ue_PyUObject *)ue_py_object->py_object )->ue_property = ue_py_object;
390+
391+ if (PythonGCManager && PythonGCManager->IsValidLowLevel ()) {
392+ PythonGCManager->python_properties_gc .Add (ue_py_object);
393+ }
394+ else {
395+ UE_LOG (LogPython, Fatal, TEXT (" [BUG] PYTHON GC MANAGER BROKEN DURING ADD !" ));
396+ }
397+
390398#if UEPY_MEMORY_DEBUG
391- UE_LOG (LogPython, Warning, TEXT (" ----------------- CREATED %p " ), ue_py_object);
399+ UE_LOG (LogPython, Warning, TEXT (" CREATED UPyObject at %p for %p " ), ue_py_object, ue_obj );
392400#endif
393401 // Py_INCREF(ue_py_object->py_object);
394402 }
395- #if UEPY_MEMORY_DEBUG
396- UE_LOG (LogPython, Warning, TEXT (" REFCNT for %s = %d" ), *ue_obj->GetName (), ue_py_object->py_object ->ob_base .ob_refcnt );
397- #endif
403+
398404 return ue_py_object->py_object ;
399405}
400406
@@ -557,13 +563,10 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) {
557563
558564 // map a UStruct to a dictionary (if possible)
559565 if (auto casted_prop = Cast<UStructProperty>(prop)) {
560- UE_LOG (LogPython, Error, TEXT (" UStruct type not supported for %s" ), *prop->GetName ());
561566 if (auto casted_struct = Cast<UScriptStruct>(casted_prop->Struct )) {
562- UE_LOG (LogPython, Error, TEXT (" UStruct scripted !!!" ));
563567 PyObject *py_struct_dict = PyDict_New ();
564568 TFieldIterator<UProperty> SArgs (casted_struct);
565569 for (; SArgs; ++SArgs) {
566- UE_LOG (LogPython, Error, TEXT (" UStruct NAME %s" ), *SArgs->GetName ());
567570 PyObject *struct_value = ue_py_convert_property (*SArgs, casted_prop->ContainerPtrToValuePtr <uint8>(buffer));
568571 if (!struct_value) {
569572 Py_DECREF (py_struct_dict);
@@ -709,12 +712,12 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_
709712
710713 FScriptDelegate script_delegate;
711714 UPythonDelegate *py_delegate = NewObject<UPythonDelegate>();
712- // py_delegate->SetPyCallable(py_callable);
715+ py_delegate->SetPyCallable (py_callable);
713716 py_delegate->SetSignature (casted_prop->SignatureFunction );
717+ // avoid delegates to be destroyed by the GC
714718 py_delegate->AddToRoot ();
715- // collect pointer to allow removal
716- PyList_Append (u_obj->python_delegates_gc , PyLong_FromLongLong ((long long )py_delegate));
717-
719+ u_obj->ue_property ->python_delegates_gc .Add (py_delegate);
720+
718721 // fake UFUNCTION for bypassing checks
719722 script_delegate.BindUFunction (py_delegate, FName (" PyFakeCallable" ));
720723
@@ -735,10 +738,16 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_
735738}
736739
737740UPyObject::~UPyObject () {
741+ #if UEPY_MEMORY_DEBUG
742+ UE_LOG (LogPython, Error, TEXT (" Destroying PyProperty %p" ), this );
743+ #endif
744+ for (UPythonDelegate *py_delegate :this ->python_delegates_gc ) {
745+ py_delegate->RemoveFromRoot ();
746+ }
738747#if UEPY_MEMORY_DEBUG
739748 if (py_object && py_object->ob_base .ob_refcnt != 1 ) {
740749 UE_LOG (LogPython, Error, TEXT (" INCONSISTENT DESTROY %p with refcnt %d" ), this , py_object->ob_base .ob_refcnt );
741750 }
742751#endif
743752 Py_XDECREF (py_object);
744- }
753+ }
0 commit comments