Skip to content

Commit e6dbd66

Browse files
author
Roberto De Ioris
committed
added support for UMapProperty
1 parent bf4aac0 commit e6dbd66

File tree

3 files changed

+225
-11
lines changed

3 files changed

+225
-11
lines changed

Source/PythonConsole/Public/PyFactory.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,45 @@ class UPyFactory : public UFactory
1414
return PyFactoryCreateFile(Class, InParent, InName.ToString(), Filename);
1515
}
1616

17+
virtual UObject* FactoryCreateNew(UClass * Class, UObject *InParent, FName InName, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) override {
18+
return PyFactoryCreateNew(Class, InParent, InName.ToString());
19+
}
20+
1721
virtual UClass* ResolveSupportedClass() override {
1822
if (SupportedClass)
1923
return SupportedClass;
2024
return PyResolveSupportedClass();
2125
}
2226

27+
virtual FText GetDisplayName() const override {
28+
if (!PyDisplayName.IsEmpty()) {
29+
return FText::FromString(PyDisplayName);
30+
}
31+
return FText::FromString(GetClass()->GetName().Replace(UTF8_TO_TCHAR("Factory"), UTF8_TO_TCHAR("")));
32+
}
33+
34+
virtual FText GetToolTip() const override {
35+
if (!PyToolTip.IsEmpty()) {
36+
return FText::FromString(PyToolTip);
37+
}
38+
return GetDisplayName();
39+
}
40+
2341
UFUNCTION(BlueprintImplementableEvent)
2442
UObject* PyFactoryCreateFile(class UClass * Class, UObject *InParent, const FString & InName, const FString & Filename);
2543

44+
UFUNCTION(BlueprintImplementableEvent)
45+
UObject* PyFactoryCreateNew(class UClass * Class, UObject *InParent, const FString & InName);
46+
2647
UFUNCTION(BlueprintImplementableEvent)
2748
UClass* PyResolveSupportedClass();
2849

50+
UPROPERTY(BlueprintReadWrite, Category = "PyFactory")
51+
FString PyDisplayName;
52+
53+
UPROPERTY(BlueprintReadWrite, Category = "PyFactory")
54+
FString PyToolTip;
55+
2956
};
3057

3158

Source/UnrealEnginePython/Private/UEPyModule.cpp

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,54 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k
12121212

12131213
}
12141214
}
1215+
#if ENGINE_MINOR_VERSION >= 15
1216+
else if (PyDict_Check(value)) {
1217+
if (PyDict_Size(value) == 1) {
1218+
PyObject *py_key = nullptr;
1219+
PyObject *py_value = nullptr;
1220+
Py_ssize_t pos = 0;
1221+
PyDict_Next(value, &pos, &py_key, &py_value);
1222+
if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) {
1223+
PyObject *first_item = nullptr;
1224+
PyObject *second_item = nullptr;
1225+
1226+
ue_PyUObject *py_obj = (ue_PyUObject *)py_key;
1227+
if (py_obj->ue_object->IsA<UClass>()) {
1228+
UClass *p_class = (UClass *)py_obj->ue_object;
1229+
if (p_class->IsChildOf<UProperty>()) {
1230+
first_item = py_key;
1231+
}
1232+
else {
1233+
first_item = (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass());
1234+
}
1235+
}
1236+
else if (py_obj->ue_object->IsA<UScriptStruct>()) {
1237+
first_item = (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass());
1238+
}
1239+
1240+
ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value;
1241+
if (py_obj2->ue_object->IsA<UClass>()) {
1242+
UClass *p_class = (UClass *)py_obj2->ue_object;
1243+
if (p_class->IsChildOf<UProperty>()) {
1244+
second_item = py_value;
1245+
}
1246+
else {
1247+
second_item = (PyObject *)ue_get_python_wrapper(UObjectProperty::StaticClass());
1248+
}
1249+
}
1250+
else if (py_obj2->ue_object->IsA<UScriptStruct>()) {
1251+
second_item = (PyObject *)ue_get_python_wrapper(UStructProperty::StaticClass());
1252+
}
1253+
1254+
if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) {
1255+
unreal_engine_py_log_error();
1256+
return -1;
1257+
}
1258+
prop_added = true;
1259+
}
1260+
}
1261+
}
1262+
#endif
12151263
// function ?
12161264
else if (PyCallable_Check(value) && class_key[0] >= 'A' && class_key[0] <= 'Z') {
12171265
uint32 func_flags = FUNC_Native | FUNC_BlueprintCallable | FUNC_Public;
@@ -1825,20 +1873,18 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer) {
18251873

18261874
PyObject *py_dict = PyDict_New();
18271875

1828-
int32 num = map_helper.Num();
1829-
for (int32 i = 0; num; i++) {
1876+
for (int32 i = 0; i < map_helper.Num(); i++) {
18301877
if (map_helper.IsValidIndex(i)) {
1831-
num--;
18321878

1833-
uint8 *key = map_helper.GetKeyPtr(i);
1834-
PyObject *py_key = ue_py_convert_property(map_helper.KeyProp, key);
1879+
uint8 *ptr = map_helper.GetPairPtr(i);
1880+
1881+
PyObject *py_key = ue_py_convert_property(map_helper.KeyProp, ptr);
18351882
if (!py_key) {
18361883
Py_DECREF(py_dict);
18371884
return NULL;
18381885
}
18391886

1840-
uint8 *value = map_helper.GetValuePtr(i);
1841-
PyObject *py_value = ue_py_convert_property(map_helper.ValueProp, value);
1887+
PyObject *py_value = ue_py_convert_property(map_helper.ValueProp, ptr);
18421888
if (!py_value) {
18431889
Py_DECREF(py_dict);
18441890
return NULL;
@@ -2032,6 +2078,38 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer) {
20322078
return false;
20332079
}
20342080

2081+
#if ENGINE_MINOR_VERSION >= 15
2082+
if (PyDict_Check(py_obj)) {
2083+
if (auto casted_prop = Cast<UMapProperty>(prop)) {
2084+
FScriptMapHelper_InContainer map_helper(casted_prop, buffer);
2085+
2086+
PyObject *py_key = nullptr;
2087+
PyObject *py_value = nullptr;
2088+
Py_ssize_t pos = 0;
2089+
2090+
map_helper.EmptyValues();
2091+
while (PyDict_Next(py_obj, &pos, &py_key, &py_value)) {
2092+
2093+
int32 index = map_helper.AddDefaultValue_Invalid_NeedsRehash();
2094+
uint8 *ptr = map_helper.GetPairPtr(index);
2095+
2096+
if (!ue_py_convert_pyobject(py_key, casted_prop->KeyProp, ptr)) {
2097+
return false;
2098+
}
2099+
2100+
if (!ue_py_convert_pyobject(py_value, casted_prop->ValueProp, ptr)) {
2101+
return false;
2102+
}
2103+
}
2104+
map_helper.Rehash();
2105+
2106+
return true;
2107+
}
2108+
2109+
return false;
2110+
}
2111+
#endif
2112+
20352113
// structs
20362114

20372115
if (ue_PyFVector *py_vec = py_ue_is_fvector(py_obj)) {

Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -775,19 +775,27 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
775775
PyObject *obj;
776776
char *name;
777777
PyObject *property_class = nullptr;
778-
if (!PyArg_ParseTuple(args, "Os|O:add_property", &obj, &name, &property_class)) {
778+
PyObject *property_class2 = nullptr;
779+
if (!PyArg_ParseTuple(args, "Os|OO:add_property", &obj, &name, &property_class, &property_class2)) {
779780
return NULL;
780781
}
781782

782783
if (!self->ue_object->IsA<UStruct>())
783784
return PyErr_Format(PyExc_Exception, "uobject is not a UStruct");
784785

785786
UObject *scope = nullptr;
787+
786788
UProperty *u_property = nullptr;
787789
UClass *u_class = nullptr;
790+
UProperty *u_property2 = nullptr;
791+
UClass *u_class2 = nullptr;
792+
788793
UClass *u_prop_class = nullptr;
789794
UScriptStruct *u_script_struct = nullptr;
795+
UClass *u_prop_class2 = nullptr;
796+
UScriptStruct *u_script_struct2 = nullptr;
790797
bool is_array = false;
798+
bool is_map = false;
791799

792800
if (property_class) {
793801
if (!ue_is_pyuobject(property_class)) {
@@ -805,6 +813,22 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
805813
}
806814
}
807815

816+
if (property_class2) {
817+
if (!ue_is_pyuobject(property_class2)) {
818+
return PyErr_Format(PyExc_Exception, "property class arg is not a uobject");
819+
}
820+
ue_PyUObject *py_prop_class = (ue_PyUObject *)property_class2;
821+
if (py_prop_class->ue_object->IsA<UClass>()) {
822+
u_prop_class2 = (UClass *)py_prop_class->ue_object;
823+
}
824+
else if (py_prop_class->ue_object->IsA<UScriptStruct>()) {
825+
u_script_struct2 = (UScriptStruct *)py_prop_class->ue_object;
826+
}
827+
else {
828+
return PyErr_Format(PyExc_Exception, "property class arg is not a UClass or a UScriptStruct");
829+
}
830+
}
831+
808832
EObjectFlags o_flags = RF_Public | RF_MarkAsNative;// | RF_Transient;
809833

810834
if (ue_is_pyuobject(obj)) {
@@ -840,6 +864,44 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
840864
}
841865
Py_DECREF(py_item);
842866
}
867+
#if ENGINE_MINOR_VERSION >= 15
868+
else if (PyList_Size(obj) == 2) {
869+
PyObject *py_key = PyList_GetItem(obj, 0);
870+
PyObject *py_value = PyList_GetItem(obj, 1);
871+
if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) {
872+
// KEY
873+
ue_PyUObject *py_obj = (ue_PyUObject *)py_key;
874+
if (!py_obj->ue_object->IsA<UClass>()) {
875+
return PyErr_Format(PyExc_Exception, "uobject is not a UClass");
876+
}
877+
u_class = (UClass *)py_obj->ue_object;
878+
if (!u_class->IsChildOf<UProperty>())
879+
return PyErr_Format(PyExc_Exception, "uobject is not a UProperty");
880+
if (u_class == UArrayProperty::StaticClass())
881+
return PyErr_Format(PyExc_Exception, "please use a two-items list of properties for maps");
882+
883+
// VALUE
884+
ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value;
885+
if (!py_obj2->ue_object->IsA<UClass>()) {
886+
return PyErr_Format(PyExc_Exception, "uobject is not a UClass");
887+
}
888+
u_class2 = (UClass *)py_obj2->ue_object;
889+
if (!u_class2->IsChildOf<UProperty>())
890+
return PyErr_Format(PyExc_Exception, "uobject is not a UProperty");
891+
if (u_class2 == UArrayProperty::StaticClass())
892+
return PyErr_Format(PyExc_Exception, "please use a two-items list of properties for maps");
893+
894+
895+
UMapProperty *u_map = NewObject<UMapProperty>(self->ue_object, UTF8_TO_TCHAR(name), o_flags);
896+
if (!u_map)
897+
return PyErr_Format(PyExc_Exception, "unable to allocate new UProperty");
898+
scope = u_map;
899+
is_map = true;
900+
}
901+
Py_DECREF(py_key);
902+
Py_DECREF(py_value);
903+
}
904+
#endif
843905
}
844906

845907

@@ -849,7 +911,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
849911

850912
u_property = NewObject<UProperty>(scope, u_class, UTF8_TO_TCHAR(name), o_flags);
851913
if (!u_property) {
852-
if (is_array)
914+
if (is_array || is_map)
853915
scope->MarkPendingKill();
854916
return PyErr_Format(PyExc_Exception, "unable to allocate new UProperty");
855917
}
@@ -888,6 +950,53 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
888950
u_property = u_array;
889951
}
890952

953+
#if ENGINE_MINOR_VERSION >= 15
954+
if (is_map) {
955+
u_property2 = NewObject<UProperty>(scope, u_class2, NAME_None, o_flags);
956+
if (!u_property2) {
957+
if (is_array || is_map)
958+
scope->MarkPendingKill();
959+
return PyErr_Format(PyExc_Exception, "unable to allocate new UProperty");
960+
}
961+
UMapProperty *u_map = (UMapProperty *)scope;
962+
963+
964+
u_property->SetPropertyFlags(flags);
965+
u_property2->SetPropertyFlags(flags);
966+
967+
if (u_property->GetClass() == UObjectProperty::StaticClass()) {
968+
UObjectProperty *obj_prop = (UObjectProperty *)u_property;
969+
if (u_prop_class) {
970+
obj_prop->SetPropertyClass(u_prop_class);
971+
}
972+
}
973+
if (u_property->GetClass() == UStructProperty::StaticClass()) {
974+
UStructProperty *obj_prop = (UStructProperty *)u_property;
975+
if (u_script_struct) {
976+
obj_prop->Struct = u_script_struct;
977+
}
978+
}
979+
980+
if (u_property2->GetClass() == UObjectProperty::StaticClass()) {
981+
UObjectProperty *obj_prop = (UObjectProperty *)u_property2;
982+
if (u_prop_class2) {
983+
obj_prop->SetPropertyClass(u_prop_class2);
984+
}
985+
}
986+
if (u_property2->GetClass() == UStructProperty::StaticClass()) {
987+
UStructProperty *obj_prop = (UStructProperty *)u_property2;
988+
if (u_script_struct2) {
989+
obj_prop->Struct = u_script_struct2;
990+
}
991+
}
992+
993+
u_map->KeyProp = u_property;
994+
u_map->ValueProp = u_property2;
995+
996+
u_property = u_map;
997+
}
998+
#endif
999+
8911000
if (u_class == UMulticastDelegateProperty::StaticClass()) {
8921001
UMulticastDelegateProperty *mcp = (UMulticastDelegateProperty *)u_property;
8931002
mcp->SignatureFunction = NewObject<UFunction>(self->ue_object, NAME_None, RF_Public | RF_Transient | RF_MarkAsNative);
@@ -906,7 +1015,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
9061015

9071016
else if (u_class == UObjectProperty::StaticClass()) {
9081017
// ensure it is not an arry as we have already managed it !
909-
if (!is_array) {
1018+
if (!is_array && !is_map) {
9101019
UObjectProperty *obj_prop = (UObjectProperty *)u_property;
9111020
if (u_prop_class) {
9121021
obj_prop->SetPropertyClass(u_prop_class);
@@ -916,7 +1025,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) {
9161025

9171026
else if (u_class == UStructProperty::StaticClass()) {
9181027
// ensure it is not an arry as we have already managed it !
919-
if (!is_array) {
1028+
if (!is_array && !is_map) {
9201029
UStructProperty *obj_prop = (UStructProperty *)u_property;
9211030
if (u_script_struct) {
9221031
obj_prop->Struct = u_script_struct;

0 commit comments

Comments
 (0)