@@ -2940,7 +2940,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject *
29402940 for (; PArgs && ((PArgs->PropertyFlags & CPF_Parm) == CPF_Parm); ++PArgs)
29412941 {
29422942 UProperty *prop = *PArgs;
2943- if (prop-> PropertyFlags & CPF_OutParm )
2943+ if (PROP_IS_OUT_PARAM ( prop) )
29442944 {
29452945 if (prop->IsA <UArrayProperty>() || prop->HasAnyPropertyFlags (CPF_ConstParm) == false )
29462946 num_out_params++;
@@ -2999,7 +2999,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject *
29992999 for (; OProps; ++OProps)
30003000 {
30013001 UProperty *prop = *OProps;
3002- if (prop-> HasAnyPropertyFlags (CPF_OutParm) && ( prop-> IsA <UArrayProperty>() || prop-> HasAnyPropertyFlags (CPF_ConstParm) == false ))
3002+ if (PROP_IS_OUT_PARAM ( prop))
30033003 {
30043004 PyObject *py_out = ue_py_convert_property (prop, buffer, 0 );
30053005 if (!py_out)
@@ -3076,6 +3076,25 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_
30763076 Py_RETURN_NONE;
30773077}
30783078
3079+ UProperty *new_property_from_pyobject (UObject *owner, const char *prop_name, PyObject *value);
3080+
3081+ // used by new_property_from_pyobject to create an array property of the given name and type, or nullptr if it couldn't
3082+ // be created for some reason. Array properties can be created using type annotations in either of the following
3083+ // forms: arg:[arrayType] or arg:typing.List[arrayType]
3084+ // where arrayType is a type that new_property_from_pyobject knows how to handle
3085+ UProperty *new_array_property (UObject *owner, const char *prop_name, PyObject *py_array_type)
3086+ {
3087+ UArrayProperty *array_prop = NewObject<UArrayProperty>(owner, UTF8_TO_TCHAR (prop_name), RF_Public);
3088+ UProperty *inner = new_property_from_pyobject (array_prop, " Inner" , py_array_type);
3089+ if (!inner)
3090+ {
3091+ UE_LOG (LogPython, Error, TEXT (" Unsupported type for list property %s" ), UTF8_TO_TCHAR (*prop_name));
3092+ return nullptr ;
3093+ }
3094+ array_prop->Inner = inner;
3095+ return array_prop;
3096+ }
3097+
30793098// Creates and configures a UProperty on the given owner using info from a PyObject (typically a type
30803099// object) representing function parameter or return value type info, or nullptr if the given type is unsupported.
30813100UProperty *new_property_from_pyobject (UObject *owner, const char *prop_name, PyObject *value)
@@ -3158,7 +3177,22 @@ UProperty *new_property_from_pyobject(UObject *owner, const char *prop_name, PyO
31583177 UE_LOG (LogPython, Error, TEXT (" exactly one class is allowed in type info for %s" ), UTF8_TO_TCHAR (*owner->GetName ()));
31593178 return nullptr ;
31603179 }
3180+ PyObject *type_name = PyObject_GetAttrString (value, " __name__" );
3181+ if (!type_name)
3182+ {
3183+ UE_LOG (LogPython, Error, TEXT (" failed to get type object name from %s" ), UTF8_TO_TCHAR (prop_name));
3184+ return nullptr ;
3185+ }
3186+
3187+ // If the annotation was like "foo:typing.List[int]" (i.e. __name__ == 'List') then it's an array property. Anything
3188+ // else (like foo:typing.List[Pawn]) will be treated as a class property
31613189 PyObject *py_class = PyTuple_GetItem (type_args, 0 );
3190+ bool is_array = !strcmp (PyUnicode_AsUTF8 (type_name), " List" );
3191+ Py_DECREF (type_name);
3192+
3193+ if (is_array)
3194+ return new_array_property (owner, prop_name, py_class);
3195+
31623196 ue_PyUObject *py_obj = ue_is_pyuobject (py_class);
31633197 if (!py_obj)
31643198 {
@@ -3179,6 +3213,17 @@ UProperty *new_property_from_pyobject(UObject *owner, const char *prop_name, PyO
31793213 Py_DECREF (type_args);
31803214 }
31813215 }
3216+ else if (PyList_Check (value))
3217+ {
3218+ // has to be a single item list containing something that can be used as a property
3219+ if (PyList_Size (value) != 1 )
3220+ {
3221+ UE_LOG (LogPython, Error, TEXT (" List property %s must have a single item providing the array type" ), UTF8_TO_TCHAR (prop_name));
3222+ return nullptr ;
3223+ }
3224+
3225+ return new_array_property (owner, prop_name, PyList_GetItem (value, 0 ));
3226+ }
31823227 else if (ue_PyUObject *py_obj = ue_is_pyuobject (value))
31833228 {
31843229 if (py_obj->ue_object ->IsA <UClass>())
@@ -3286,7 +3331,15 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_
32863331 UProperty *prop = new_property_from_pyobject (function, p_name, value);
32873332 if (prop)
32883333 {
3289- prop->SetPropertyFlags (CPF_Parm);
3334+ uint64 flags = CPF_Parm;
3335+ if (prop->IsA <UArrayProperty>())
3336+ { // some weirdness to mimic what the engine does: arrays are always passed by reference, so we need to mark this as
3337+ // a reference parameter. But the definition for CPF_ReferenceParm says that CPF_OutParm should be set too. So we
3338+ // mark it as an out param even though it isn't, flag it as const to indicate that it's gotta be an input param,
3339+ // and then on the calling side skip it.
3340+ flags |= CPF_ReferenceParm | CPF_OutParm | CPF_ConstParm;
3341+ }
3342+ prop->SetPropertyFlags (flags);
32903343 *next_property = prop;
32913344 next_property = &prop->Next ;
32923345 *next_property_link = prop;
@@ -3319,7 +3372,7 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_
33193372 return_param_index = cur_index;
33203373 break ;
33213374 }
3322- if (p-> PropertyFlags & CPF_OutParm )
3375+ if (PROP_IS_OUT_PARAM (p) )
33233376 cur_index++;
33243377 ++It;
33253378 }
0 commit comments