2626
2727
2828#include " PythonDelegate.h"
29+ #include " PythonFunction.h"
2930
3031
3132DEFINE_LOG_CATEGORY (LogPython);
@@ -68,7 +69,7 @@ static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args) {
6869 ue_PyUObject *py_obj = ue_python_gc.at (u_obj);
6970 Py_DECREF (py_obj);
7071 }
71-
72+
7273 return PyLong_FromLong (broken_list.size ());
7374
7475}
@@ -370,8 +371,8 @@ static PyMethodDef ue_PyUObject_methods[] = {
370371 { " sequencer_possessables" , (PyCFunction)py_ue_sequencer_possessables, METH_VARARGS , " " },
371372 { " sequencer_find_possessable" , (PyCFunction)py_ue_sequencer_find_possessable, METH_VARARGS , " " },
372373 { " sequencer_add_master_track" , (PyCFunction)py_ue_sequencer_add_master_track, METH_VARARGS , " " },
373-
374-
374+
375+
375376#if PY_MAJOR_VERSION >= 3
376377 { " add_function" , (PyCFunction)py_ue_add_function, METH_VARARGS , " " },
377378#endif
@@ -540,8 +541,84 @@ static PyTypeObject ue_PyUObjectType = {
540541 ue_PyUObject_methods, /* tp_methods */
541542};
542543
544+ UClass *unreal_engine_new_uclass (char *name, UClass *outer_parent) {
545+ UObject *outer = GetTransientPackage ();
546+ UClass *parent = UObject::StaticClass ();
547+
548+ if (outer_parent) {
549+ parent = outer_parent;
550+ outer = parent->GetOuter ();
551+ }
552+ UClass *new_object = NewObject<UClass>(outer, UTF8_TO_TCHAR (name), RF_Public | RF_Transient | RF_MarkAsNative);
553+ if (!new_object)
554+ return nullptr ;
555+
556+ new_object->ClassConstructor = parent->ClassConstructor ;
557+ new_object->SetSuperStruct (parent);
558+
559+ new_object->PropertyLink = parent->PropertyLink ;
560+ new_object->ClassWithin = parent->ClassWithin ;
561+ new_object->ClassConfigName = parent->ClassConfigName ;
562+
563+ new_object->ClassFlags |= (parent->ClassFlags & (CLASS_Inherit | CLASS_ScriptInherit));
564+ new_object->ClassFlags |= CLASS_Native;
543565
566+ new_object->ClassCastFlags = parent->ClassCastFlags ;
567+
568+ new_object->Bind ();
569+ new_object->StaticLink (true );
570+
571+ new_object->GetDefaultObject ();
572+
573+ return new_object;
574+ }
575+
576+ static int unreal_engine_py_init (ue_PyUObject *self, PyObject *args, PyObject *kwds) {
577+ // is it subclassing ?
578+ if (PyTuple_Size (args) == 3 ) {
579+ UE_LOG (LogPython, Warning, TEXT (" %s" ), UTF8_TO_TCHAR (PyUnicode_AsUTF8 (PyObject_Str (PyTuple_GetItem (args, 0 )))));
580+ UE_LOG (LogPython, Warning, TEXT (" %s" ), UTF8_TO_TCHAR (PyUnicode_AsUTF8 (PyObject_Str (PyTuple_GetItem (args, 1 )))));
581+ UE_LOG (LogPython, Warning, TEXT (" %s" ), UTF8_TO_TCHAR (PyUnicode_AsUTF8 (PyObject_Str (PyTuple_GetItem (args, 2 )))));
582+
583+ PyObject *parents = PyTuple_GetItem (args, 1 );
584+ ue_PyUObject *parent = (ue_PyUObject *)PyTuple_GetItem (parents, 0 );
585+
586+ PyObject *class_attributes = PyTuple_GetItem (args, 2 );
587+
588+ PyObject *class_name = PyDict_GetItemString (class_attributes, (char *)" __qualname__" );
589+ char *name = PyUnicode_AsUTF8 (class_name);
590+ // check if parent is a uclass
591+ UClass *new_class = unreal_engine_new_uclass (name, (UClass *)parent->ue_object );
592+ if (!new_class)
593+ return -1 ;
594+ UE_LOG (LogPython, Warning, TEXT (" %s" ), *parent->ue_object ->GetName ());
595+
596+ PyObject *class_attributes_keys = PyObject_GetIter (class_attributes);
597+ for (;;) {
598+ PyObject *key = PyIter_Next (class_attributes_keys);
599+ if (!key) {
600+ if (PyErr_Occurred ())
601+ return -1 ;
602+ break ;
603+ }
604+ if (!PyUnicode_Check (key))
605+ continue ;
606+ char *class_key = PyUnicode_AsUTF8 (key);
607+ if (strlen (class_key) > 2 && class_key[0 ] == ' _' && class_key[1 ] == ' _' )
608+ continue ;
609+ PyObject *value = PyDict_GetItem (class_attributes, key);
610+ if (PyCallable_Check (value)) {
611+ if (!unreal_engine_add_function (new_class, class_key, value, FUNC_Native | FUNC_BlueprintCallable | FUNC_Public)) {
612+ UE_LOG (LogPython, Error, TEXT (" unable to add function %s" ), UTF8_TO_TCHAR (class_key));
613+ return -1 ;
614+ }
615+ }
616+ }
544617
618+ self->ue_object = new_class;
619+ }
620+ return 0 ;
621+ }
545622
546623void unreal_engine_init_py_module () {
547624#if PY_MAJOR_VERSION >= 3
@@ -563,6 +640,7 @@ void unreal_engine_init_py_module() {
563640
564641
565642 ue_PyUObjectType.tp_new = PyType_GenericNew;
643+ ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init;
566644 if (PyType_Ready (&ue_PyUObjectType) < 0 )
567645 return ;
568646
@@ -735,7 +813,7 @@ void unreal_engine_py_log_error() {
735813 }
736814
737815 PyErr_Clear ();
738- }
816+ }
739817
740818// retrieve a UWorld from a generic UObject (if possible)
741819UWorld *ue_get_uworld (ue_PyUObject *py_obj) {
@@ -1299,3 +1377,171 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_
12991377 Py_INCREF (Py_None);
13001378 return Py_None;
13011379}
1380+
1381+ UFunction *unreal_engine_add_function (UClass *u_class, char *name, PyObject *py_callable, uint32 function_flags) {
1382+
1383+ if (u_class->FindFunctionByName (UTF8_TO_TCHAR (name))) {
1384+ UE_LOG (LogPython, Error, TEXT (" function %s is already registered" ), UTF8_TO_TCHAR (name));
1385+ return nullptr ;
1386+ }
1387+
1388+ UFunction *parent_function = u_class->GetSuperClass ()->FindFunctionByName (UTF8_TO_TCHAR (name));
1389+
1390+ UPythonFunction *function = NewObject<UPythonFunction>(u_class, UTF8_TO_TCHAR (name), RF_Public | RF_Transient | RF_MarkAsNative);
1391+ function->SetPyCallable (py_callable);
1392+
1393+ function->RepOffset = MAX_uint16;
1394+ function->ReturnValueOffset = MAX_uint16;
1395+ function->FirstPropertyToInit = NULL ;
1396+ function->Script .Add (EX_EndFunctionParms);
1397+
1398+
1399+ if (parent_function) {
1400+ function->SetSuperStruct (parent_function);
1401+ /*
1402+ TODO: duplicate properties of parent
1403+ */
1404+ }
1405+ else {
1406+
1407+ // iterate all arguments using inspect.signature()
1408+ // this is required to maintaining args order
1409+ PyObject *inspect = PyImport_ImportModule (" inspect" );
1410+ if (!inspect) {
1411+ return NULL ;
1412+ }
1413+ PyObject *signature = PyObject_CallMethod (inspect, " signature" , " O" , py_callable);
1414+ if (!signature) {
1415+ return NULL ;
1416+ }
1417+
1418+ PyObject *parameters = PyObject_GetAttrString (signature, " parameters" );
1419+ if (!parameters) {
1420+ return NULL ;
1421+ }
1422+
1423+ PyObject *annotations = PyObject_GetAttrString (py_callable, " __annotations__" );
1424+
1425+ UField **next_property = &function->Children ;
1426+ UProperty **next_property_link = &function->PropertyLink ;
1427+
1428+ PyObject *parameters_keys = PyObject_GetIter (parameters);
1429+ // do not process args if no annotations are available
1430+ while (annotations) {
1431+ PyObject *key = PyIter_Next (parameters_keys);
1432+ if (!key) {
1433+ if (PyErr_Occurred ())
1434+ return NULL ;
1435+ break ;
1436+ }
1437+ if (!PyUnicode_Check (key))
1438+ continue ;
1439+
1440+ char *p_name = PyUnicode_AsUTF8 (key);
1441+
1442+ PyObject *value = PyDict_GetItem (annotations, key);
1443+ if (!value)
1444+ continue ;
1445+ UProperty *prop = nullptr ;
1446+ if (PyType_Check (value)) {
1447+ if ((PyTypeObject *)value == &PyFloat_Type) {
1448+ prop = NewObject<UFloatProperty>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1449+ }
1450+ else if ((PyTypeObject *)value == &PyUnicode_Type) {
1451+ prop = NewObject<UStrProperty>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1452+ }
1453+ else if ((PyTypeObject *)value == &PyBool_Type) {
1454+ prop = NewObject<UBoolProperty>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1455+ }
1456+ else if ((PyTypeObject *)value == &PyLong_Type) {
1457+ prop = NewObject<UInt64Property>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1458+ }
1459+ // TODO add native types (like vectors, rotators...)
1460+ }
1461+ if (prop) {
1462+ prop->SetPropertyFlags (CPF_Parm);
1463+ *next_property = prop;
1464+ next_property = &prop->Next ;
1465+ *next_property_link = prop;
1466+ next_property_link = &prop->PropertyLinkNext ;
1467+ }
1468+ else {
1469+ UE_LOG (LogPython, Warning, TEXT (" Unable to map argument %s to function %s" ), UTF8_TO_TCHAR (p_name), UTF8_TO_TCHAR (name));
1470+ }
1471+ }
1472+
1473+ // check for return value
1474+ if (annotations) {
1475+ PyObject *py_return_value = PyDict_GetItemString (annotations, " return" );
1476+ if (py_return_value) {
1477+ UProperty *prop = nullptr ;
1478+ if (PyType_Check (py_return_value)) {
1479+ char *p_name = (char *) " ReturnValue" ;
1480+ if ((PyTypeObject *)py_return_value == &PyFloat_Type) {
1481+ prop = NewObject<UFloatProperty>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1482+ }
1483+ else if ((PyTypeObject *)py_return_value == &PyUnicode_Type) {
1484+ prop = NewObject<UStrProperty>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1485+ }
1486+ else if ((PyTypeObject *)py_return_value == &PyBool_Type) {
1487+ prop = NewObject<UBoolProperty>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1488+ }
1489+ else if ((PyTypeObject *)py_return_value == &PyLong_Type) {
1490+ prop = NewObject<UInt64Property>(function, UTF8_TO_TCHAR (p_name), RF_Public);
1491+ }
1492+ // TODO add native types (like vectors, rotators...)
1493+ }
1494+ if (prop) {
1495+ prop->SetPropertyFlags (CPF_Parm | CPF_ReturnParm);
1496+ *next_property = prop;
1497+ next_property = &prop->Next ;
1498+ *next_property_link = prop;
1499+ next_property_link = &prop->PropertyLinkNext ;
1500+ }
1501+ else {
1502+ UE_LOG (LogPython, Warning, TEXT (" Unable to map return value to function %s" ), UTF8_TO_TCHAR (name));
1503+ }
1504+ }
1505+ }
1506+ }
1507+
1508+ function->Bind ();
1509+ function->StaticLink (true );
1510+
1511+ function->ParmsSize = 0 ;
1512+ function->NumParms = 0 ;
1513+
1514+ // allocate properties storage (ignore super)
1515+ TFieldIterator<UProperty> props (function, EFieldIteratorFlags::ExcludeSuper);
1516+ for (; props; ++props) {
1517+ UProperty *p = *props;
1518+ if (p->HasAnyPropertyFlags (CPF_Parm)) {
1519+ function->NumParms ++;
1520+ function->ParmsSize = p->GetOffset_ForUFunction () + p->GetSize ();
1521+ if (p->HasAnyPropertyFlags (CPF_ReturnParm)) {
1522+ function->ReturnValueOffset = p->GetOffset_ForUFunction ();
1523+ }
1524+ }
1525+ }
1526+
1527+ UE_LOG (LogPython, Warning, TEXT (" REGISTERED FUNCTION %s WITH %d PARAMS (size %d) %d" ), *function->GetFName ().ToString (), function->NumParms , function->ParmsSize , function->PropertiesSize );
1528+
1529+ function->FunctionFlags = function_flags;
1530+ // function->FunctionFlags |= FUNC_Native | FUNC_Event | FUNC_BlueprintEvent;
1531+
1532+ function->SetNativeFunc ((Native)&UPythonFunction::CallPythonCallable);
1533+
1534+ function->Next = u_class->Children ;
1535+ u_class->Children = function;
1536+ u_class->AddFunctionToFunctionMap (function);
1537+
1538+ u_class->StaticLink (true );
1539+
1540+ // regenerate CDO
1541+ u_class->GetDefaultObject ()->RemoveFromRoot ();
1542+ u_class->GetDefaultObject ()->MarkPendingKill ();
1543+ u_class->ClassDefaultObject = nullptr ;
1544+ u_class->GetDefaultObject ();
1545+
1546+ return function;
1547+ }
0 commit comments