From f8e0a91d42fede4c1d603845364fb9726cc10acf Mon Sep 17 00:00:00 2001 From: F0x06 Date: Sat, 4 May 2019 01:29:11 +0200 Subject: [PATCH 01/31] FRotator arguments order should be `pitch` `yaw` `roll` instead of `roll` `pitch` `yaw` --- .../Private/Wrappers/UEPyFRotator.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp index 3558faa5b..f4b2d35c6 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp @@ -89,8 +89,8 @@ static PyGetSetDef ue_PyFRotator_getseters[] = { static PyObject *ue_PyFRotator_str(ue_PyFRotator *self) { - return PyUnicode_FromFormat("", - PyFloat_FromDouble(self->rot.Roll), PyFloat_FromDouble(self->rot.Pitch), PyFloat_FromDouble(self->rot.Yaw)); + return PyUnicode_FromFormat("", + PyFloat_FromDouble(self->rot.Pitch), PyFloat_FromDouble(self->rot.Yaw), PyFloat_FromDouble(self->rot.Roll)); } PyTypeObject ue_PyFRotatorType = { @@ -211,11 +211,11 @@ static Py_ssize_t ue_py_frotator_seq_length(ue_PyFRotator *self) { static PyObject *ue_py_frotator_seq_item(ue_PyFRotator *self, Py_ssize_t i) { switch (i) { case 0: - return PyFloat_FromDouble(self->rot.Roll); - case 1: return PyFloat_FromDouble(self->rot.Pitch); - case 2: + case 1: return PyFloat_FromDouble(self->rot.Yaw); + case 2: + return PyFloat_FromDouble(self->rot.Roll); } return PyErr_Format(PyExc_IndexError, "FRotator has only 3 items"); } @@ -232,7 +232,7 @@ static int ue_py_frotator_init(ue_PyFRotator *self, PyObject *args, PyObject *kw } } - if (!PyArg_ParseTuple(args, "|fff", &roll, &pitch, &yaw)) + if (!PyArg_ParseTuple(args, "|fff", &pitch, &yaw, &roll)) return -1; if (PyTuple_Size(args) == 1) { @@ -297,7 +297,7 @@ bool py_ue_rotator_arg(PyObject *args, FRotator &rot) { } float pitch, yaw, roll; - if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) + if (!PyArg_ParseTuple(args, "fff", &pitch, &yaw, &roll)) return false; rot.Pitch = pitch; rot.Yaw = yaw; From bfdea2aa187b6c662d8c35e7f6d90fa7939abdde Mon Sep 17 00:00:00 2001 From: David Date: Mon, 15 Jun 2020 14:05:38 +0100 Subject: [PATCH 02/31] Merge pull request #718 from F0x06/frotator-args-fix FRotator arguments order should be pitch, yaw, roll instead of roll, pitch, yaw From 3ff694eb48777f1a1744a8d93f737148734f8a8f Mon Sep 17 00:00:00 2001 From: David Date: Mon, 15 Jun 2020 14:16:24 +0100 Subject: [PATCH 03/31] Added define for FRotator argument order if really want original UnrealEnginePython order. --- .../Private/Wrappers/UEPyFRotator.cpp | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp index f4b2d35c6..6e50318a0 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp @@ -1,5 +1,10 @@ #include "UEPyFRotator.h" + +// use this definition if want original argument order for FRotator +//#define USE_UNREALENGINEPYTHON_ORDER 1 + + static PyObject *py_ue_frotator_get_vector(ue_PyFRotator *self, PyObject * args) { FVector vec = self->rot.Vector(); return py_ue_new_fvector(vec); @@ -89,8 +94,14 @@ static PyGetSetDef ue_PyFRotator_getseters[] = { static PyObject *ue_PyFRotator_str(ue_PyFRotator *self) { +#ifdef USE_UNREALENGINEPYTHON_ORDER + return PyUnicode_FromFormat("", + PyFloat_FromDouble(self->rot.Roll), PyFloat_FromDouble(self->rot.Pitch), PyFloat_FromDouble(self->rot.Yaw)); + +#else return PyUnicode_FromFormat("", PyFloat_FromDouble(self->rot.Pitch), PyFloat_FromDouble(self->rot.Yaw), PyFloat_FromDouble(self->rot.Roll)); +#endif } PyTypeObject ue_PyFRotatorType = { @@ -210,12 +221,22 @@ static Py_ssize_t ue_py_frotator_seq_length(ue_PyFRotator *self) { static PyObject *ue_py_frotator_seq_item(ue_PyFRotator *self, Py_ssize_t i) { switch (i) { + +#ifdef USE_UNREALENGINEPYTHON_ORDER + case 0: + return PyFloat_FromDouble(self->rot.Roll); + case 1: + return PyFloat_FromDouble(self->rot.Pitch); + case 2: + return PyFloat_FromDouble(self->rot.Yaw); +#else case 0: return PyFloat_FromDouble(self->rot.Pitch); case 1: return PyFloat_FromDouble(self->rot.Yaw); case 2: return PyFloat_FromDouble(self->rot.Roll); +#endif } return PyErr_Format(PyExc_IndexError, "FRotator has only 3 items"); } @@ -232,8 +253,13 @@ static int ue_py_frotator_init(ue_PyFRotator *self, PyObject *args, PyObject *kw } } +#ifdef USE_UNREALENGINEPYTHON_ORDER + if (!PyArg_ParseTuple(args, "|fff", &roll, &pitch, &yaw)) + return -1; +#else if (!PyArg_ParseTuple(args, "|fff", &pitch, &yaw, &roll)) return -1; +#endif if (PyTuple_Size(args) == 1) { yaw = pitch; @@ -297,8 +323,13 @@ bool py_ue_rotator_arg(PyObject *args, FRotator &rot) { } float pitch, yaw, roll; +#ifdef USE_UNREALENGINEPYTHON_ORDER + if (!PyArg_ParseTuple(args, "fff", &roll, &pitch, &yaw)) + return false; +#else if (!PyArg_ParseTuple(args, "fff", &pitch, &yaw, &roll)) return false; +#endif rot.Pitch = pitch; rot.Yaw = yaw; rot.Roll = roll; From 7114954e6632ddabc61087a43bec9807d3974601 Mon Sep 17 00:00:00 2001 From: "Ra.h / SaxonRah / Robert Chubb" Date: Sat, 15 Feb 2020 19:10:35 -0500 Subject: [PATCH 04/31] Updated to Unreal Engine 4.24.2 Fixed and Removed all Errors and Warnings. Tested a few python scripts, ran editor for long time. Seems to be okay. --- .../PythonAutomation.Build.cs | 3 +- Source/PythonConsole/PythonConsole.Build.cs | 2 +- Source/PythonEditor/Private/PythonEditor.cpp | 1 + .../Private/PythonProjectEditor.cpp | 11 +++++- Source/PythonEditor/PythonEditor.Build.cs | 4 +- .../UEPyFMaterialEditorUtilities.cpp | 7 +++- .../Private/Slate/UEPyFMenuBuilder.cpp | 10 +++-- .../UnrealEnginePython/Private/UEPyEditor.cpp | 21 +++++++--- .../UnrealEnginePython/Private/UEPyModule.cpp | 4 +- .../Private/UObject/UEPyActor.cpp | 2 +- .../Private/UObject/UEPyMaterial.cpp | 6 +-- .../Private/UObject/UEPySequencer.cpp | 29 +++++++++----- .../Private/UObject/UEPyStaticMesh.cpp | 4 +- .../Private/UObject/UEPyTransform.cpp | 6 +-- .../Private/UObject/UEPyViewport.cpp | 39 +++++++++++-------- .../UnrealEnginePython.Build.cs | 16 ++++---- 16 files changed, 107 insertions(+), 58 deletions(-) diff --git a/Source/PythonAutomation/PythonAutomation.Build.cs b/Source/PythonAutomation/PythonAutomation.Build.cs index e2f80b814..6e5127cd6 100644 --- a/Source/PythonAutomation/PythonAutomation.Build.cs +++ b/Source/PythonAutomation/PythonAutomation.Build.cs @@ -13,8 +13,7 @@ public PythonAutomation(TargetInfo Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; string enableUnityBuild = System.Environment.GetEnvironmentVariable("UEP_ENABLE_UNITY_BUILD"); - bFasterWithoutUnity = string.IsNullOrEmpty(enableUnityBuild); - + bUseUnity = string.IsNullOrEmpty(enableUnityBuild); PrivateIncludePaths.AddRange( new string[] { "PythonConsole/Private", diff --git a/Source/PythonConsole/PythonConsole.Build.cs b/Source/PythonConsole/PythonConsole.Build.cs index 29476f1dc..2065442b2 100644 --- a/Source/PythonConsole/PythonConsole.Build.cs +++ b/Source/PythonConsole/PythonConsole.Build.cs @@ -13,7 +13,7 @@ public PythonConsole(TargetInfo Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; string enableUnityBuild = System.Environment.GetEnvironmentVariable("UEP_ENABLE_UNITY_BUILD"); - bFasterWithoutUnity = string.IsNullOrEmpty(enableUnityBuild); + bUseUnity = string.IsNullOrEmpty(enableUnityBuild); PrivateIncludePaths.AddRange( new string[] { diff --git a/Source/PythonEditor/Private/PythonEditor.cpp b/Source/PythonEditor/Private/PythonEditor.cpp index 42bbf9e5e..00d8cb9f4 100644 --- a/Source/PythonEditor/Private/PythonEditor.cpp +++ b/Source/PythonEditor/Private/PythonEditor.cpp @@ -7,6 +7,7 @@ #include "PythonEditorStyle.h" #include "PythonProjectEditor.h" #include "PythonProject.h" +#include "Subsystems/AssetEditorSubsystem.h" #include "Runtime/Slate/Public/Framework/MultiBox/MultiBoxBuilder.h" static const FName PythonEditorTabName( TEXT( "PythonEditor" ) ); diff --git a/Source/PythonEditor/Private/PythonProjectEditor.cpp b/Source/PythonEditor/Private/PythonProjectEditor.cpp index 33b2a56c8..1d17d5bf5 100644 --- a/Source/PythonEditor/Private/PythonProjectEditor.cpp +++ b/Source/PythonEditor/Private/PythonProjectEditor.cpp @@ -1,6 +1,11 @@ // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #include "PythonProjectEditor.h" + +#include "Subsystems/AssetEditorSubsystem.h" + +// #include "UnrealEd.h" + #include "SPythonEditor.h" #include "SPythonProjectEditor.h" #include "Runtime/Slate/Public/Widgets/Docking/SDockTab.h" @@ -13,6 +18,8 @@ #include "PythonProjectEditorCommands.h" #include "Runtime/Core/Public/HAL/PlatformFilemanager.h" #include "Runtime/Core/Public/Misc/MessageDialog.h" + + #define LOCTEXT_NAMESPACE "PythonEditor" TWeakPtr FPythonProjectEditor::PythonEditor; @@ -209,7 +216,9 @@ void FPythonProjectEditor::RegisterToolbarTab(const TSharedRef& InitToolkitHost, class UPythonProject* PythonProject) { - FAssetEditorManager::Get().CloseOtherEditors(PythonProject, this); + // UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + // GEditor->GetEditorSubsystem()->CloseOtherEditors(PythonProject, this); + GEditor->GetEditorSubsystem()->CloseOtherEditors(PythonProject, this); PythonProjectBeingEdited = PythonProject; TSharedPtr ThisPtr(SharedThis(this)); diff --git a/Source/PythonEditor/PythonEditor.Build.cs b/Source/PythonEditor/PythonEditor.Build.cs index 11b024164..bba5cb41b 100644 --- a/Source/PythonEditor/PythonEditor.Build.cs +++ b/Source/PythonEditor/PythonEditor.Build.cs @@ -13,7 +13,7 @@ public PythonEditor(TargetInfo Target) PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; string enableUnityBuild = System.Environment.GetEnvironmentVariable("UEP_ENABLE_UNITY_BUILD"); - bFasterWithoutUnity = string.IsNullOrEmpty(enableUnityBuild); + bUseUnity = string.IsNullOrEmpty(enableUnityBuild); PrivateIncludePaths.AddRange( new string[] { @@ -32,11 +32,13 @@ public PythonEditor(TargetInfo Target) "UnrealEd", "EditorStyle", "PropertyEditor", + "ContentBrowser", "Kismet", // for FWorkflowCentricApplication "InputCore", "DirectoryWatcher", "LevelEditor", "Projects", + "Engine", "UnrealEnginePython" } ); diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp index 0b80f1466..0cd0bdf4d 100644 --- a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp @@ -1,7 +1,10 @@ #include "UEPyFMaterialEditorUtilities.h" + #if WITH_EDITOR +#include "Subsystems/AssetEditorSubsystem.h" + #include "Materials/Material.h" #include "Runtime/Engine/Classes/EdGraph/EdGraph.h" @@ -54,7 +57,9 @@ static PyObject *py_ue_command_apply(PyObject *cls, PyObject * args) return PyErr_Format(PyExc_Exception, "argument is not a UMaterial"); } - IAssetEditorInstance *Instance = FAssetEditorManager::Get().FindEditorForAsset(Material, false); + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + + IAssetEditorInstance *Instance = GEditor->GetEditorSubsystem()->FindEditorForAsset(Material, false); if (!Instance) { return PyErr_Format(PyExc_Exception, "unable to retrieve editor for UMaterial"); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 3e0b978ed..3e4b73c11 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -1,5 +1,5 @@ #include "UEPyFMenuBuilder.h" - +#include "IAssetTools.h" #include "Wrappers/UEPyESlateEnums.h" static PyObject* py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder* self, PyObject* args) @@ -115,6 +115,8 @@ static PyObject* py_ue_fmenu_builder_add_menu_separator(ue_PyFMenuBuilder* self, } #if WITH_EDITOR +#include "ContentBrowserModule.h" + static PyObject* py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder* self, PyObject* args) { PyObject* py_assets; @@ -140,8 +142,10 @@ static PyObject* py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder* self, Py_DECREF(py_assets); FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked(TEXT("AssetTools")); - bool addedSomething = AssetToolsModule.Get().GetAssetActions(u_objects, self->menu_builder, true); - if (addedSomething) + + FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); + TArray& AssetMenuExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders(); + if (AssetMenuExtenderDelegates.Num() > 0) { Py_RETURN_TRUE; } diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 1b3df7bca..350611c2c 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -12,6 +12,7 @@ #include "UnrealEd.h" #include "FbxMeshUtils.h" #include "Kismet2/BlueprintEditorUtils.h" + #include "Editor/LevelEditor/Public/LevelEditorActions.h" #include "Editor/UnrealEd/Public/EditorLevelUtils.h" #include "Runtime/Projects/Public/Interfaces/IPluginManager.h" @@ -41,6 +42,7 @@ #include "UEPyIPlugin.h" + PyObject *py_unreal_engine_redraw_all_viewports(PyObject * self, PyObject * args) { FEditorSupportDelegates::RedrawAllViewports.Broadcast(); @@ -1140,7 +1142,9 @@ PyObject *py_unreal_engine_get_selected_assets(PyObject * self, PyObject * args) PyObject *py_unreal_engine_get_all_edited_assets(PyObject * self, PyObject * args) { - TArray assets = FAssetEditorManager::Get().GetAllEditedAssets(); + + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + TArray assets = GEditor->GetEditorSubsystem()->GetAllEditedAssets(); PyObject *assets_list = PyList_New(0); for (UObject *asset : assets) @@ -1168,7 +1172,9 @@ PyObject *py_unreal_engine_open_editor_for_asset(PyObject * self, PyObject * arg if (!u_obj) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); - if (FAssetEditorManager::Get().OpenEditorForAsset(u_obj)) + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + + if (GEditor->GetEditorSubsystem()->OpenEditorForAsset(u_obj)) { Py_RETURN_TRUE; } @@ -1193,7 +1199,8 @@ PyObject *py_unreal_engine_find_editor_for_asset(PyObject * self, PyObject * arg if (py_bool && PyObject_IsTrue(py_bool)) bFocus = true; - IAssetEditorInstance *instance = FAssetEditorManager::Get().FindEditorForAsset(u_obj, bFocus); + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + IAssetEditorInstance *instance = GEditor->GetEditorSubsystem()->FindEditorForAsset(u_obj, bFocus); if (!instance) return PyErr_Format(PyExc_Exception, "no editor found for asset"); @@ -1212,14 +1219,16 @@ PyObject *py_unreal_engine_close_editor_for_asset(PyObject * self, PyObject * ar UObject *u_obj = ue_py_check_type(py_obj); if (!u_obj) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); - FAssetEditorManager::Get().CloseAllEditorsForAsset(u_obj); + + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + GEditor->GetEditorSubsystem()->CloseAllEditorsForAsset(u_obj); Py_RETURN_NONE; } PyObject *py_unreal_engine_close_all_asset_editors(PyObject * self, PyObject * args) { - FAssetEditorManager::Get().CloseAllAssetEditors(); + GEditor->GetEditorSubsystem()->CloseAllAssetEditors(); Py_RETURN_NONE; } @@ -1355,7 +1364,7 @@ PyObject *py_unreal_engine_reload_blueprint(PyObject * self, PyObject * args) UBlueprint *reloaded_bp = nullptr; Py_BEGIN_ALLOW_THREADS - reloaded_bp = FKismetEditorUtilities::ReloadBlueprint(bp); + reloaded_bp = FKismetEditorUtilities::ReplaceBlueprint(bp, bp); Py_END_ALLOW_THREADS Py_RETURN_UOBJECT(reloaded_bp); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 56954d32e..7fb3a139a 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -203,8 +203,8 @@ static PyObject* py_ue_get_py_proxy(ue_PyUObject* self, PyObject* args) static PyObject* py_unreal_engine_shutdown(PyObject* self, PyObject* args) { - GIsRequestingExit = true; - + // GIsRequestingExit = true; + RequestEngineExit(FString(TEXT("I'm Shutting Down, Dave..."))); Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index e319bcc94..cdde15840 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -649,7 +649,7 @@ PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * a UObject *ret_obj = nullptr; Py_BEGIN_ALLOW_THREADS; - ret_obj = actor->CreateDefaultSubobject(FName(UTF8_TO_TCHAR(name)), UObject::StaticClass(), u_class, false, false, true); + ret_obj = actor->CreateDefaultSubobject(FName(UTF8_TO_TCHAR(name)), UObject::StaticClass(), u_class, false, true); Py_END_ALLOW_THREADS; if (!ret_obj) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index c958094f6..efe80cc65 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -511,7 +511,7 @@ PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * FMeshSectionInfo info = mesh->SectionInfoMap.Get(lod_index, material_index); #endif info.bEnableCollision = enabled; - mesh->SectionInfoMap.Set(lod_index, material_index, info); + mesh->GetSectionInfoMap().Set(lod_index, material_index, info); mesh->MarkPackageDirty(); @@ -545,9 +545,9 @@ PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * ar enabled = true; } - FMeshSectionInfo info = mesh->SectionInfoMap.Get(lod_index, material_index); + FMeshSectionInfo info = mesh->GetSectionInfoMap().Get(lod_index, material_index); info.bCastShadow = enabled; - mesh->SectionInfoMap.Set(lod_index, material_index, info); + mesh->GetSectionInfoMap().Set(lod_index, material_index, info); mesh->MarkPackageDirty(); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 8858a7936..65419111e 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -8,6 +8,7 @@ #include "Runtime/LevelSequence/Public/LevelSequence.h" #if WITH_EDITOR +#include "Subsystems/AssetEditorSubsystem.h" #include "Editor/Sequencer/Public/ISequencer.h" #include "Editor/Sequencer/Public/ISequencerModule.h" #include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" @@ -209,9 +210,9 @@ static bool ImportFBXTransform(FString NodeName, UMovieScene3DTransformSection* #endif #if WITH_EDITOR + PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) { - ue_py_check(self); PyObject *py_bool = nullptr; @@ -227,13 +228,17 @@ PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) ULevelSequence *seq = (ULevelSequence *)self->ue_object; + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + if (py_bool && PyObject_IsTrue(py_bool)) { // try to open the editor for the asset - FAssetEditorManager::Get().OpenEditorForAsset(seq); + // FAssetEditorManager::Get().OpenEditorForAsset(seq); + GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); } - IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + // IAssetEditorInstance* editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + IAssetEditorInstance* editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(seq, true); if (editor) { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; @@ -464,9 +469,11 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) actors.Add((AActor *)py_ue_obj->ue_object); // try to open the editor for the asset - FAssetEditorManager::Get().OpenEditorForAsset(seq); - IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(seq, true); if (editor) { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; @@ -519,9 +526,11 @@ PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * arg UActorComponent* actorComponent = (UActorComponent *)py_ue_obj->ue_object; // try to open the editor for the asset - FAssetEditorManager::Get().OpenEditorForAsset(seq); - IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + AssetEditorSubsystem->OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = AssetEditorSubsystem->FindEditorForAsset(seq, true); FGuid new_guid; if (editor) { @@ -568,9 +577,11 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args ULevelSequence *seq = (ULevelSequence *)self->ue_object; // try to open the editor for the asset - FAssetEditorManager::Get().OpenEditorForAsset(seq); - IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); + //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); + GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(seq, true); if (!editor) { return PyErr_Format(PyExc_Exception, "unable to access sequencer"); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp index eaf1a1cf8..43b4cf434 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp @@ -124,10 +124,10 @@ PyObject *py_ue_static_mesh_get_raw_mesh(ue_PyUObject *self, PyObject * args) FRawMesh raw_mesh; - if (lod_index < 0 || lod_index >= mesh->SourceModels.Num()) + if (lod_index < 0 || lod_index >= mesh->GetSourceModels().Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index"); - mesh->SourceModels[lod_index].RawMeshBulkData->LoadRawMesh(raw_mesh); + mesh->GetSourceModel(lod_index).RawMeshBulkData->LoadRawMesh(raw_mesh); return py_ue_new_fraw_mesh(raw_mesh); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp index 34ae1eade..70793e038 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp @@ -466,7 +466,7 @@ PyObject *py_ue_get_relative_location(ue_PyUObject *self, PyObject * args) ue_py_check(self); if (self->ue_object->IsA()) { - FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeLocation; + FVector vec3 = ((USceneComponent *)self->ue_object)->GetRelativeLocation(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); @@ -477,7 +477,7 @@ PyObject *py_ue_get_relative_rotation(ue_PyUObject *self, PyObject * args) ue_py_check(self); if (self->ue_object->IsA()) { - FRotator rot = ((USceneComponent *)self->ue_object)->RelativeRotation; + FRotator rot = ((USceneComponent *)self->ue_object)->GetRelativeRotation(); return py_ue_new_frotator(rot); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); @@ -488,7 +488,7 @@ PyObject *py_ue_get_relative_scale(ue_PyUObject *self, PyObject * args) ue_py_check(self); if (self->ue_object->IsA()) { - FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeScale3D; + FVector vec3 = ((USceneComponent *)self->ue_object)->GetRelativeScale3D(); return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp index 4e7244b63..1b2d1e5a0 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp @@ -4,6 +4,8 @@ #include "LevelEditor.h" #include "Editor/LevelEditor/Public/ILevelViewport.h" #include "Editor/UnrealEd/Public/LevelEditorViewport.h" +#include "SLevelViewport.h" + #endif #include "Slate/UEPySWidget.h" @@ -59,14 +61,15 @@ PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args return NULL; } - FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); - if (!EditorModule.GetFirstActiveViewport().IsValid()) + if (!ActiveLevelViewport.IsValid()) return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); - FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); - viewport_client.SetViewMode((EViewModeIndex)mode); + LevelViewportClient.SetViewMode((EViewModeIndex)mode); Py_RETURN_NONE; } @@ -81,14 +84,16 @@ PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * a return NULL; } - FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); - if (!EditorModule.GetFirstActiveViewport().IsValid()) + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); + + if (!ActiveLevelViewport.IsValid()) return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); - FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); - viewport_client.SetCameraSpeedSetting(speed); + LevelViewportClient.SetCameraSpeedSetting(speed); Py_RETURN_NONE; } @@ -107,14 +112,15 @@ PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * if (!vector) return PyErr_Format(PyExc_Exception, "argument is not a FVector"); - FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); - if (!EditorModule.GetFirstActiveViewport().IsValid()) + if (!ActiveLevelViewport.IsValid()) return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); - FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); - viewport_client.SetViewLocation(vector->vec); + LevelViewportClient.SetViewLocation(vector->vec); Py_RETURN_NONE; } @@ -133,14 +139,15 @@ PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * if (!rotator) return PyErr_Format(PyExc_Exception, "argument is not a FRotator"); - FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); - if (!EditorModule.GetFirstActiveViewport().IsValid()) + if (!ActiveLevelViewport.IsValid()) return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); - FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); - viewport_client.SetViewRotation(rotator->rot); + LevelViewportClient.SetViewRotation(rotator->rot); Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index a6fe71946..ee8070ba7 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -95,8 +95,9 @@ public UnrealEnginePython(TargetInfo Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + PublicDefinitions.Add("WITH_UNREALENGINEPYTHON=1"); // fixed string enableUnityBuild = System.Environment.GetEnvironmentVariable("UEP_ENABLE_UNITY_BUILD"); - bFasterWithoutUnity = string.IsNullOrEmpty(enableUnityBuild); + bUseUnity = string.IsNullOrEmpty(enableUnityBuild); PublicIncludePaths.AddRange( new string[] { @@ -213,7 +214,7 @@ public UnrealEnginePython(TargetInfo Target) System.Console.WriteLine("Using Python at: " + pythonHome); PublicIncludePaths.Add(pythonHome); string libPath = GetWindowsPythonLibFile(pythonHome); - PublicLibraryPaths.Add(Path.GetDirectoryName(libPath)); + PublicSystemLibraryPaths.Add(Path.GetDirectoryName(libPath)); PublicAdditionalLibraries.Add(libPath); } else if (Target.Platform == UnrealTargetPlatform.Mac) @@ -229,7 +230,7 @@ public UnrealEnginePython(TargetInfo Target) System.Console.WriteLine("Using Python at: " + pythonHome); PublicIncludePaths.Add(pythonHome); string libPath = GetMacPythonLibFile(pythonHome); - PublicLibraryPaths.Add(Path.GetDirectoryName(libPath)); + PublicAdditionalLibraries.Add(Path.GetDirectoryName(libPath)); PublicDelayLoadDLLs.Add(libPath); } else if (Target.Platform == UnrealTargetPlatform.Linux) @@ -259,14 +260,15 @@ public UnrealEnginePython(TargetInfo Target) } #if WITH_FORWARDED_MODULE_RULES_CTOR else if (Target.Platform == UnrealTargetPlatform.Android) - { + { PublicIncludePaths.Add(System.IO.Path.Combine(ModuleDirectory, "../../android/python35/include")); - PublicLibraryPaths.Add(System.IO.Path.Combine(ModuleDirectory, "../../android/armeabi-v7a")); + PublicAdditionalLibraries.Add(System.IO.Path.Combine(ModuleDirectory, "../../android/armeabi-v7a")); PublicAdditionalLibraries.Add("python3.5m"); string APLName = "UnrealEnginePython_APL.xml"; - string RelAPLPath = Utils.MakePathRelativeTo(System.IO.Path.Combine(ModuleDirectory, APLName), Target.RelativeEnginePath); - AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", RelAPLPath)); + string RelAPLPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath); + AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(RelAPLPath, APLName)); + } #endif From eb84e8ce9eb4cc6af5e4a1402b006c2b4cb6d666 Mon Sep 17 00:00:00 2001 From: Robert Chubb Date: Sat, 15 Feb 2020 19:14:01 -0500 Subject: [PATCH 05/31] Update README.md Fixed Version Number --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68ed706f1..a86c9f35b 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Once the plugin is installed and enabled, you get access to the 'PythonConsole' All of the exposed engine features are under the 'unreal_engine' virtual module (it is completely coded in c into the plugin, so do not expect to run 'import unreal_engine' from a standard python shell) -The minimal supported Unreal Engine version is 4.12, while the latest is 4.23 +The minimal supported Unreal Engine version is 4.12, while the latest is 4.24 We support official python.org releases as well as IntelPython and Anaconda distributions. From 607132eb3a9c57dda03ad37741555859ae5b9c3e Mon Sep 17 00:00:00 2001 From: David Date: Wed, 6 May 2020 15:10:40 +1000 Subject: [PATCH 06/31] Patched up differences from following merge into my master. Merge branch 'master' of https://github.com/SaxonRah/UnrealEnginePython into support4.25 --- .../Private/Slate/UEPyFMenuBuilder.cpp | 1 + .../Private/UnrealEnginePython.cpp | 20 +++++++++++++++++-- .../Private/hosts-per-state.py | 13 ++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/hosts-per-state.py diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 3e4b73c11..29e9e07b2 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -115,6 +115,7 @@ static PyObject* py_ue_fmenu_builder_add_menu_separator(ue_PyFMenuBuilder* self, } #if WITH_EDITOR + #include "ContentBrowserModule.h" static PyObject* py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder* self, PyObject* args) diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 211124849..41113d733 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -47,7 +47,6 @@ const char *ue4_module_options = "linux_global_symbols"; #include "Android/AndroidApplication.h" #endif - const char *UEPyUnicode_AsUTF8(PyObject *py_str) { #if PY_MAJOR_VERSION < 3 @@ -249,7 +248,11 @@ void FUnrealEnginePythonModule::StartupModule() if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("Home"), PythonHome, GEngineIni)) { #if PY_MAJOR_VERSION >= 3 + #if ENGINE_MINOR_VERSION >= 20 + wchar_t *home = (wchar_t *)(TCHAR_TO_WCHAR(*PythonHome)); + #else wchar_t *home = (wchar_t *)*PythonHome; + #endif #else char *home = TCHAR_TO_UTF8(*PythonHome); #endif @@ -263,7 +266,11 @@ void FUnrealEnginePythonModule::StartupModule() FPaths::NormalizeFilename(PythonHome); PythonHome = FPaths::ConvertRelativePathToFull(PythonHome); #if PY_MAJOR_VERSION >= 3 + #if ENGINE_MINOR_VERSION >= 20 + wchar_t *home = (wchar_t *)(TCHAR_TO_WCHAR(*PythonHome)); + #else wchar_t *home = (wchar_t *)*PythonHome; + #endif #else char *home = TCHAR_TO_UTF8(*PythonHome); #endif @@ -277,7 +284,11 @@ void FUnrealEnginePythonModule::StartupModule() if (GConfig->GetString(UTF8_TO_TCHAR("Python"), UTF8_TO_TCHAR("ProgramName"), IniValue, GEngineIni)) { #if PY_MAJOR_VERSION >= 3 + #if ENGINE_MINOR_VERSION >= 20 + wchar_t *program_name = (wchar_t *)(TCHAR_TO_WCHAR(*IniValue)); + #else wchar_t *program_name = (wchar_t *)*IniValue; + #endif #else char *program_name = TCHAR_TO_UTF8(*IniValue); #endif @@ -290,7 +301,11 @@ void FUnrealEnginePythonModule::StartupModule() FPaths::NormalizeFilename(IniValue); IniValue = FPaths::ConvertRelativePathToFull(IniValue); #if PY_MAJOR_VERSION >= 3 + #if ENGINE_MINOR_VERSION >= 20 + wchar_t *program_name = (wchar_t *)(TCHAR_TO_WCHAR(*IniValue)); + #else wchar_t *program_name = (wchar_t *)*IniValue; + #endif #else char *program_name = TCHAR_TO_UTF8(*IniValue); #endif @@ -391,6 +406,7 @@ void FUnrealEnginePythonModule::StartupModule() } // Setup our own paths for PYTHONPATH + #if PLATFORM_WINDOWS TArray OurPythonPaths = { PythonHome, FPaths::Combine(PythonHome, TEXT("Lib")), @@ -403,10 +419,10 @@ void FUnrealEnginePythonModule::StartupModule() PathVars.Append(OurPythonPaths); FString ModifiedPath = FString::Join(PathVars, PathDelimiter); FPlatformMisc::SetEnvironmentVar(TEXT("PATH"), *ModifiedPath); + #endif } - #if PY_MAJOR_VERSION >= 3 init_unreal_engine_builtin(); #if PLATFORM_ANDROID diff --git a/Source/UnrealEnginePython/Private/hosts-per-state.py b/Source/UnrealEnginePython/Private/hosts-per-state.py new file mode 100644 index 000000000..4b1fa8084 --- /dev/null +++ b/Source/UnrealEnginePython/Private/hosts-per-state.py @@ -0,0 +1,13 @@ +""" Get lists of event hosts per state """ +from datetime import datetime, timezone, timedelta +import json +import csv +import sys +import copy + +import pytz +import pygsheets +import requests + +from auth.auth import create_drive_api, create_sheets_api, get_mobilize_credentials +import utils From d0b0a726d54a9baf74210aa073e36cf2f19cd9b8 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 25 Jun 2020 13:29:05 +0100 Subject: [PATCH 07/31] Remove hosts-per-state.py from Source - does not seem appropriate place. --- .../UnrealEnginePython/Private/hosts-per-state.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 Source/UnrealEnginePython/Private/hosts-per-state.py diff --git a/Source/UnrealEnginePython/Private/hosts-per-state.py b/Source/UnrealEnginePython/Private/hosts-per-state.py deleted file mode 100644 index 4b1fa8084..000000000 --- a/Source/UnrealEnginePython/Private/hosts-per-state.py +++ /dev/null @@ -1,13 +0,0 @@ -""" Get lists of event hosts per state """ -from datetime import datetime, timezone, timedelta -import json -import csv -import sys -import copy - -import pytz -import pygsheets -import requests - -from auth.auth import create_drive_api, create_sheets_api, get_mobilize_credentials -import utils From fca059741b1d56a71164f8a69e826940e46055da Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 20:47:57 +0100 Subject: [PATCH 08/31] Removed the IAssetTools.h - it failed to build with 4.25 and commented it built on 4.18 and 4.25 (on Linux). --- Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 29e9e07b2..4554f4521 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -1,5 +1,5 @@ #include "UEPyFMenuBuilder.h" -#include "IAssetTools.h" +//#include "IAssetTools.h" #include "Wrappers/UEPyESlateEnums.h" static PyObject* py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder* self, PyObject* args) From e3ebc3aaf7e2127a87f806a6452a83a554d7a382 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 18:24:51 +0100 Subject: [PATCH 09/31] Wrapped some 4.24 updates with appropriate ENGINE_MINOR_VERSION so still builds on 4.18. Builds on 4.18 and 4.25 (only versions tested). --- Source/UnrealEnginePython/Private/UEPyEditor.cpp | 16 ++++++++++++++++ Source/UnrealEnginePython/Private/UEPyEngine.cpp | 4 ++++ Source/UnrealEnginePython/Private/UEPyModule.cpp | 5 ++++- .../Private/UObject/UEPyActor.cpp | 10 +++++++++- .../Private/UObject/UEPySequencer.cpp | 8 ++++++++ .../Private/UObject/UEPyTransform.cpp | 12 ++++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 350611c2c..bfe7b6d9c 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -1297,7 +1297,11 @@ PyObject *py_unreal_engine_create_blueprint(PyObject * self, PyObject * args) TArray TopLevelPackages; TopLevelPackages.Add(outer); +#if ENGINE_MINOR_VERSION >= 21 + if (!UPackageTools::HandleFullyLoadingPackages(TopLevelPackages, FText::FromString("Create a new object"))) +#else if (!PackageTools::HandleFullyLoadingPackages(TopLevelPackages, FText::FromString("Create a new object"))) +#endif return PyErr_Format(PyExc_Exception, "unable to fully load package"); if (FindObject(outer, UTF8_TO_TCHAR(bp_name)) != nullptr) @@ -2024,7 +2028,11 @@ PyObject *py_ue_factory_create_new(ue_PyUObject *self, PyObject * args) return PyErr_Format(PyExc_Exception, "invalid object name"); } +#if ENGINE_MINOR_VERSION >= 21 + FString PackageName = UPackageTools::SanitizePackageName(FString(UTF8_TO_TCHAR(name))); +#else FString PackageName = PackageTools::SanitizePackageName(FString(UTF8_TO_TCHAR(name))); +#endif UPackage *outer = CreatePackage(nullptr, *PackageName); if (!outer) @@ -2032,7 +2040,11 @@ PyObject *py_ue_factory_create_new(ue_PyUObject *self, PyObject * args) TArray TopLevelPackages; TopLevelPackages.Add(outer); +#if ENGINE_MINOR_VERSION >= 21 + if (!UPackageTools::HandleFullyLoadingPackages(TopLevelPackages, FText::FromString("Create a new object"))) +#else if (!PackageTools::HandleFullyLoadingPackages(TopLevelPackages, FText::FromString("Create a new object"))) +#endif return PyErr_Format(PyExc_Exception, "unable to fully load package"); UClass *u_class = factory->GetSupportedClass(); @@ -2136,7 +2148,11 @@ PyObject *py_unreal_engine_add_level_to_world(PyObject *self, PyObject * args) if (!FPackageName::DoesPackageExist(UTF8_TO_TCHAR(name), nullptr, nullptr)) return PyErr_Format(PyExc_Exception, "package does not exist"); +#if ENGINE_MINOR_VERSION >= 21 + UClass *streaming_mode_class = ULevelStreamingDynamic::StaticClass(); +#else UClass *streaming_mode_class = ULevelStreamingKismet::StaticClass(); +#endif if (py_bool && PyObject_IsTrue(py_bool)) { streaming_mode_class = ULevelStreamingAlwaysLoaded::StaticClass(); diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 40a92d582..3deeed577 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -315,7 +315,11 @@ PyObject *py_unreal_engine_unload_package(PyObject * self, PyObject * args) } FText outErrorMsg; +#if ENGINE_MINOR_VERSION >= 21 + if (!UPackageTools::UnloadPackages({ packageToUnload }, outErrorMsg)) +#else if (!PackageTools::UnloadPackages({ packageToUnload }, outErrorMsg)) +#endif { return PyErr_Format(PyExc_Exception, "%s", TCHAR_TO_UTF8(*outErrorMsg.ToString())); } diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 7fb3a139a..bed1b96e2 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -203,8 +203,11 @@ static PyObject* py_ue_get_py_proxy(ue_PyUObject* self, PyObject* args) static PyObject* py_unreal_engine_shutdown(PyObject* self, PyObject* args) { - // GIsRequestingExit = true; +#if ENGINE_MINOR_VERSION >= 24 RequestEngineExit(FString(TEXT("I'm Shutting Down, Dave..."))); +#else + GIsRequestingExit = true; +#endif Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index cdde15840..b5d63070f 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp @@ -649,7 +649,11 @@ PyObject *py_ue_actor_create_default_subobject(ue_PyUObject * self, PyObject * a UObject *ret_obj = nullptr; Py_BEGIN_ALLOW_THREADS; +#if ENGINE_MINOR_VERSION >= 24 ret_obj = actor->CreateDefaultSubobject(FName(UTF8_TO_TCHAR(name)), UObject::StaticClass(), u_class, false, true); +#else + ret_obj = actor->CreateDefaultSubobject(FName(UTF8_TO_TCHAR(name)), UObject::StaticClass(), u_class, false, false, true); +#endif Py_END_ALLOW_THREADS; if (!ret_obj) @@ -795,7 +799,11 @@ PyObject *py_ue_get_actor_components_by_type(ue_PyUObject * self, PyObject * arg PyObject *components = PyList_New(0); +#if ENGINE_MINOR_VERSION >= 24 + for (UActorComponent *component : actor->K2_GetComponentsByClass(u_class)) +#else for (UActorComponent *component : actor->GetComponentsByClass(u_class)) +#endif { ue_PyUObject *item = ue_get_python_uobject(component); if (item) @@ -1040,4 +1048,4 @@ PyObject *py_ue_get_editor_world_counterpart_actor(ue_PyUObject * self, PyObject Py_RETURN_UOBJECT(editor_actor); } -#endif \ No newline at end of file +#endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 65419111e..50183917b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -167,7 +167,11 @@ static bool ImportFBXTransform(FString NodeName, UMovieScene3DTransformSection* FInterpCurveFloat EulerRotation[3]; FInterpCurveFloat Scale[3]; #endif +#if ENGINE_MINOR_VERSION > 21 + CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform, false); +#else CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform); +#endif TransformSection->Modify(); @@ -1670,7 +1674,11 @@ PyObject *py_ue_sequencer_import_fbx_transform(ue_PyUObject *self, PyObject * ar #endif FTransform DefaultTransform; #if ENGINE_MINOR_VERSION >= 18 +#if ENGINE_MINOR_VERSION >= 21 + CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform, false); +#else CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform); +#endif #if ENGINE_MINOR_VERSION < 20 for (int32 ChannelIndex = 0; ChannelIndex < 3; ++ChannelIndex) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp index 70793e038..f8193846b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp @@ -466,7 +466,11 @@ PyObject *py_ue_get_relative_location(ue_PyUObject *self, PyObject * args) ue_py_check(self); if (self->ue_object->IsA()) { +#if ENGINE_MINOR_VERSION >= 24 FVector vec3 = ((USceneComponent *)self->ue_object)->GetRelativeLocation(); +#else + FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeLocation; +#endif return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); @@ -477,7 +481,11 @@ PyObject *py_ue_get_relative_rotation(ue_PyUObject *self, PyObject * args) ue_py_check(self); if (self->ue_object->IsA()) { +#if ENGINE_MINOR_VERSION >= 24 FRotator rot = ((USceneComponent *)self->ue_object)->GetRelativeRotation(); +#else + FRotator rot = ((USceneComponent *)self->ue_object)->RelativeRotation; +#endif return py_ue_new_frotator(rot); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); @@ -488,7 +496,11 @@ PyObject *py_ue_get_relative_scale(ue_PyUObject *self, PyObject * args) ue_py_check(self); if (self->ue_object->IsA()) { +#if ENGINE_MINOR_VERSION >= 24 FVector vec3 = ((USceneComponent *)self->ue_object)->GetRelativeScale3D(); +#else + FVector vec3 = ((USceneComponent *)self->ue_object)->RelativeScale3D; +#endif return py_ue_new_fvector(vec3); } return PyErr_Format(PyExc_Exception, "uobject is not a USceneComponent"); From ed3499cc2164ca6291f9f1f8c36dcded46ccc695 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 23:04:40 +0100 Subject: [PATCH 10/31] More non-FProperty updates from Support4.25 pull request patched in. --- .../Private/UObject/UEPyCapture.cpp | 24 +++++++++++++++---- .../Wrappers/UEPyFEditorViewportClient.cpp | 18 +++++++++++++- .../Private/Wrappers/UEPyFRawMesh.h | 5 ++++ .../Private/Wrappers/UEPyFSoftSkinVertex.cpp | 6 ++++- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 26f2194c4..8d49beb6b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -118,8 +118,16 @@ struct FInEditorMultiCapture : TSharedFromThis UGameViewportClient::OnViewportCreated().AddRaw(this, &FInEditorMultiCapture::OnStart); FEditorDelegates::EndPIE.AddRaw(this, &FInEditorMultiCapture::OnEndPIE); - FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); - if (AudioDevice != nullptr) + //Use auto because UE4.25 changed type, but type has same interface + auto AudioDevice = GEngine->GetMainAudioDevice(); + +#if ENGINE_MINOR_VERSION >= 25 + bool bIsDeviceValid = AudioDevice.IsValid(); +#else + bool bIsDeviceValid = (AudioDevice != nullptr); +#endif + + if (bIsDeviceValid) { TransientMasterVolume = AudioDevice->GetTransientMasterVolume(); AudioDevice->SetTransientMasterVolume(0.0f); @@ -278,8 +286,16 @@ struct FInEditorMultiCapture : TSharedFromThis FObjectReader(GetMutableDefault(), BackedUpPlaySettings); - FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); - if (AudioDevice != nullptr) + //Use auto because UE4.25 changed type, but type has same interface + auto AudioDevice = GEngine->GetMainAudioDevice(); + +#if ENGINE_MINOR_VERSION >= 25 + bool bIsDeviceValid = AudioDevice.IsValid(); +#else + bool bIsDeviceValid = (AudioDevice != nullptr); +#endif + + if (bIsDeviceValid) { AudioDevice->SetTransientMasterVolume(TransientMasterVolume); } diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp index 43aff5748..8fa810c10 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFEditorViewportClient.cpp @@ -105,8 +105,24 @@ static PyObject *py_ue_feditor_viewport_client_set_realtime(ue_PyFEditorViewport if (!PyArg_ParseTuple(args, "OO", &bInRealtime, &bStoreCurrentValue)) return nullptr; +#if ENGINE_MINOR_VERSION >= 25 + // UE4.25 split this setting to 2 separate functions + if (PyObject_IsTrue(bStoreCurrentValue)) + { + //Persist across editor sessions + self->editor_viewport_client->SetRealtime(PyObject_IsTrue(bInRealtime) ? true : false); + } + else + { + //Don't persist across editor sessions + FText OverrideSourceText; + OverrideSourceText.FromString(TEXT("UnrealEnginePython plugin script")); + self->editor_viewport_client->SetRealtimeOverride(PyObject_IsTrue(bInRealtime) ? true : false, OverrideSourceText); + } +#else self->editor_viewport_client->SetRealtime(PyObject_IsTrue(bInRealtime) ? true : false, PyObject_IsTrue(bStoreCurrentValue) ? true : false); +#endif Py_RETURN_NONE; } @@ -195,4 +211,4 @@ PyObject *py_ue_new_feditor_viewport_client(TSharedRef ed new(&ret->editor_viewport_client) TSharedRef(editor_viewport_client); return (PyObject *)ret; } -#endif \ No newline at end of file +#endif diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRawMesh.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRawMesh.h index ba6ad8a90..44f4a57ea 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRawMesh.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRawMesh.h @@ -5,7 +5,12 @@ #if ENGINE_MINOR_VERSION > 13 +#if ENGINE_MINOR_VERSION < 25 #include "Developer/RawMesh/Public/RawMesh.h" +#else +#include "Runtime/RawMesh/Public/RawMesh.h" +#endif + struct ue_PyFRawMesh { diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.cpp index 64e168222..0a63a0abe 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSoftSkinVertex.cpp @@ -89,7 +89,11 @@ static int py_ue_fsoft_skin_vertex_set_tangent_z(ue_PyFSoftSkinVertex *self, PyO static PyObject *py_ue_fsoft_skin_vertex_get_influence_bones(ue_PyFSoftSkinVertex *self, void *closure) { - uint8 *data = self->ss_vertex.InfluenceBones; +#if ENGINE_MINOR_VERSION >= 25 + uint16* data = self->ss_vertex.InfluenceBones; +#else + uint8* data = self->ss_vertex.InfluenceBones; +#endif return Py_BuildValue((char*)"(iiiiiiii)", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); } From 5c96b513d04b36772445264b6fb012a59e5f6214 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 23:13:08 +0100 Subject: [PATCH 11/31] Non-FProperty updates I needed to build on 4.25. --- .../UnrealEnginePython/Private/UEPyEditor.cpp | 35 +++++++++++++++++++ .../Private/UObject/UEPyCapture.cpp | 8 +++++ .../Private/Wrappers/UEPyFSocket.h | 4 +-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index bfe7b6d9c..08c518616 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -90,8 +90,21 @@ PyObject *py_unreal_engine_editor_play_in_viewport(PyObject * self, PyObject * a if (!EditorModule.GetFirstActiveViewport().IsValid()) return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); + Py_BEGIN_ALLOW_THREADS; +#if ENGINE_MINOR_VERSION >= 25 + // this is supposed to be how it works but cant figure out where is bAtPlayerStart + // well basically because this is just ignored!! + FRequestPlaySessionParams play_params; + play_params.DestinationSlateViewport = EditorModule.GetFirstActiveViewport(); + play_params.StartLocation = v; + play_params.StartRotation = r; + // added if old 3rd arg is true + play_params.WorldType = EPlaySessionWorldType::SimulateInEditor; + GEditor->RequestPlaySession(play_params); +#else GEditor->RequestPlaySession(py_vector == nullptr, EditorModule.GetFirstActiveViewport(), true, &v, &r); +#endif Py_END_ALLOW_THREADS; Py_RETURN_NONE; @@ -112,7 +125,17 @@ PyObject *py_unreal_engine_request_play_session(PyObject * self, PyObject * args bool bSimulate = py_simulate_in_editor && PyObject_IsTrue(py_simulate_in_editor); Py_BEGIN_ALLOW_THREADS; +#if ENGINE_MINOR_VERSION >= 25 + // this is supposed to be how it works but cant figure out where is bAtPlayerStart + // well basically because this is just ignored!! + FRequestPlaySessionParams play_params; + play_params.DestinationSlateViewport = nullptr; + if (bSimulate) + play_params.WorldType = EPlaySessionWorldType::SimulateInEditor; + GEditor->RequestPlaySession(play_params); +#else GEditor->RequestPlaySession(bAtPlayerStart, nullptr, bSimulate); +#endif Py_END_ALLOW_THREADS; Py_RETURN_NONE; @@ -279,11 +302,23 @@ PyObject *py_unreal_engine_editor_play(PyObject * self, PyObject * args) } Py_BEGIN_ALLOW_THREADS; +#if ENGINE_MINOR_VERSION >= 25 + // this is supposed to be how it works but cant figure out where is bAtPlayerStart + // well basically because this is just ignored!! + const FString mobile_device = FString(""); + FRequestPlaySessionParams play_params; + play_params.StartLocation = v; + play_params.StartRotation = r; + // according to engine code this is ignored if old 3rd param is False (MobilePreview) + //play_params.MobilePreviewTargetDevice = mobile_device; + GEditor->RequestPlaySession(play_params); +#else #if ENGINE_MINOR_VERSION >= 17 const FString mobile_device = FString(""); GEditor->RequestPlaySession(&v, &r, false, false, mobile_device); #else GEditor->RequestPlaySession(&v, &r, false, false); +#endif #endif Py_END_ALLOW_THREADS; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 8d49beb6b..55bc24a0e 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -155,7 +155,15 @@ struct FInEditorMultiCapture : TSharedFromThis } +#if ENGINE_MINOR_VERSION >= 25 + // this is supposed to be how it works but cant figure out where is bAtPlayerStart + // well basically because this is just ignored!! + FRequestPlaySessionParams play_params; + play_params.DestinationSlateViewport = nullptr; + GEditor->RequestPlaySession(play_params); +#else GEditor->RequestPlaySession(true, nullptr, false); +#endif return false; } diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSocket.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSocket.h index 1f7446bd1..4978b1f24 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFSocket.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFSocket.h @@ -7,7 +7,7 @@ -typedef struct { +typedef struct _ue_PyFSocket { PyObject_HEAD /* Type-specific fields go here. */ FSocket *sock; @@ -17,4 +17,4 @@ typedef struct { } } ue_PyFSocket; -void ue_python_init_fsocket(PyObject *); \ No newline at end of file +void ue_python_init_fsocket(PyObject *); From d957ca0afc01cf3f222995a5e6fd39d42a231c14 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 23:19:44 +0100 Subject: [PATCH 12/31] Additional function wraps added while debugging blueprint node creation from example scripts for 4.25. --- .../Private/Blueprint/UEPyEdGraph.cpp | 26 +++++++++++++++++++ .../Private/Blueprint/UEPyEdGraph.h | 2 ++ .../Private/Blueprint/UEPyEdGraphPin.cpp | 7 +++++ .../UnrealEnginePython/Private/UEPyModule.cpp | 2 ++ 4 files changed, 37 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp index a3e53b7c5..91b62ea39 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp @@ -654,6 +654,32 @@ PyObject *py_ue_node_find_pin(ue_PyUObject * self, PyObject * args) return py_ue_new_edgraphpin(pin); } +PyObject *py_ue_node_set_purity(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + PyObject *py_bool = nullptr; + if (!PyArg_ParseTuple(args, "O:set_purity", &py_bool)) + { + return nullptr; + } + + UK2Node_DynamicCast *node = ue_py_check_type(self); + if (!node) + return PyErr_Format(PyExc_Exception, "uobject is not a K2Node_DynamicCast"); + + if (PyObject_IsTrue(py_bool)) + { + node->SetPurity(true); + } + else + { + node->SetPurity(false); + } + + Py_RETURN_NONE; +} + PyObject *py_ue_node_function_entry_set_pure(ue_PyUObject * self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h index e0fd0fe69..e0302287a 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h @@ -28,6 +28,8 @@ PyObject *py_ue_node_create_pin(ue_PyUObject *, PyObject *); PyObject *py_ue_node_pin_type_changed(ue_PyUObject *, PyObject *); PyObject *py_ue_node_pin_default_value_changed(ue_PyUObject *, PyObject *); +PyObject *py_ue_node_set_purity(ue_PyUObject *, PyObject *); + PyObject *py_ue_node_function_entry_set_pure(ue_PyUObject *, PyObject *); PyObject *py_ue_node_get_title(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp index 283079f31..806bca552 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp @@ -253,8 +253,15 @@ static int py_ue_edgraphpin_set_sub_category(ue_PyEdGraphPin *self, PyObject *va return -1; } +static PyObject *py_ue_edgraphpin_get_direction(ue_PyEdGraphPin *self, void *closure) +{ + uint8 direction = self->pin->Direction; + return PyLong_FromLong((long)direction); +} + static PyGetSetDef ue_PyEdGraphPin_getseters[] = { { (char*)"name", (getter)py_ue_edgraphpin_get_name, NULL, (char *)"", NULL }, + { (char*)"direction", (getter)py_ue_edgraphpin_get_direction, NULL, (char *)"", NULL }, { (char*)"category", (getter)py_ue_edgraphpin_get_category, (setter)py_ue_edgraphpin_set_category, (char *)"", NULL }, { (char*)"sub_category", (getter)py_ue_edgraphpin_get_sub_category, (setter)py_ue_edgraphpin_set_sub_category, (char *)"", NULL }, { (char*)"default_value", (getter)py_ue_edgraphpin_get_default_value, (setter)py_ue_edgraphpin_set_default_value, (char *)"", NULL }, diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index bed1b96e2..13459bd59 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -678,6 +678,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "node_pin_type_changed", (PyCFunction)py_ue_node_pin_type_changed, METH_VARARGS, "" }, { "node_pin_default_value_changed", (PyCFunction)py_ue_node_pin_default_value_changed, METH_VARARGS, "" }, + { "node_set_purity", (PyCFunction)py_ue_node_set_purity, METH_VARARGS, "" }, + { "node_function_entry_set_pure", (PyCFunction)py_ue_node_function_entry_set_pure, METH_VARARGS, "" }, { "node_allocate_default_pins", (PyCFunction)py_ue_node_allocate_default_pins, METH_VARARGS, "" }, From e23c9566876e9acda97a946fd948cf0b887c3ec4 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 23:24:38 +0100 Subject: [PATCH 13/31] Added keyword arguments for Outer and Name for UObject instance creation in python. This was used to get the blueprint node creation example scripts to work in 4.25. --- .../UnrealEnginePython/Private/UEPyModule.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 13459bd59..b2f2c9524 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1416,7 +1416,22 @@ static PyObject* ue_PyUObject_call(ue_PyUObject* self, PyObject* args, PyObject* { return NULL; } + // allow for keyword specification of Outer and Name + // because want to be able to just add Outer + // note that we do not check for other keywords being passed - they are just ignored + // for the moment override positional arguments if given in keyword form + if (kw) + { + PyObject* py_chk_outer = PyDict_GetItemString(kw, "Outer"); + if (py_chk_outer != nullptr) + py_outer = py_chk_outer; + PyObject* py_chk_name = PyDict_GetItemString(kw, "Name"); + if (py_chk_name != nullptr) + py_name = py_chk_name; + } int num_args = py_name ? 3 : 1; + if (num_args == 1 && py_outer != Py_None) + num_args = 2; PyObject* py_args = PyTuple_New(num_args); Py_INCREF((PyObject*)self); PyTuple_SetItem(py_args, 0, (PyObject*)self); @@ -1426,6 +1441,10 @@ static PyObject* ue_PyUObject_call(ue_PyUObject* self, PyObject* args, PyObject* PyTuple_SetItem(py_args, 1, py_outer); Py_INCREF(py_name); PyTuple_SetItem(py_args, 2, py_name); + } else if (py_outer != Py_None) + { + Py_INCREF(py_outer); + PyTuple_SetItem(py_args, 1, py_outer); } ue_PyUObject* ret = (ue_PyUObject*)py_unreal_engine_new_object(nullptr, py_args); Py_DECREF(py_args); From f52a2c16a388afe61c7faa760fdd0e51b4a6f0f2 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 28 Jun 2020 23:29:02 +0100 Subject: [PATCH 14/31] Missed non-FProperty update from Support4.25 pull request patched in. --- Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp index 92df02d50..110f6c127 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp @@ -11,7 +11,11 @@ static PyObject *py_ue_stext_block_set_text(ue_PySTextBlock *self, PyObject * ar return nullptr; } +#if ENGINE_MINOR_VERSION < 25 py_STextBlock->SetText(FString(UTF8_TO_TCHAR(text))); +#else + py_STextBlock->SetText(FText::FromString(FString(UTF8_TO_TCHAR(text)))); +#endif Py_RETURN_SLATE_SELF; } From 350998c92d7bab2adc6aa552d5fb02d392228f00 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 4 Jul 2020 14:20:08 +0100 Subject: [PATCH 15/31] Major update for 4.25 so can load system python shared library extension modules on debian/ubuntu eg ctypes/unittest. This may make it impossible to load Epics python and UnrealEnginePython together. --- .../Private/UnrealEnginePython.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 41113d733..742d38037 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -28,8 +28,27 @@ void unreal_engine_init_py_module(); void init_unreal_engine_builtin(); #if PLATFORM_LINUX +// so something seems to have changed between 4.18 and 4.25 which means we can no longer +// import python extension modules that have/are dynamic libraries (ie site?dist-packages modules) +// eg import ctypes fails with missing dynamic link symbol found in the primary python shared library +// it seems that the python extension modules are not able to access the primary +// python shared library /usr/lib/x86_64-linux-gnu/libpythonx.x.so symbols +// OK so this gets even more confusing as apparently on other linux distributions the python extension libraries +// ARE linked with the primary python shared library - this is a debian/ubuntu issue +// it appears what happened is that the default symbol visibility for global symbols was changed from default to hidden +// - likely to reduce chance of symbol clashes - it could be particularly true now with Unreals python implementation +// although that seems to use static linking - but this fixup may mean we cannot have both python implementations +// active +// note that ue4_module_options is a symbol whose existence and value is checked in either LinuxPlatformProcess.cpp (4.18) +// or UnixPlatformProcess.cpp (4.25) to determine if to load dynamic libraries using RTLD_GLOBAL +// - otherwise RTLD_LOCAL is used +// it apparently has to be a global symbol itself for this to work +#if ENGINE_MINOR_VERSION >= 25 +const char *ue4_module_options __attribute__((visibility("default"))) = "linux_global_symbols"; +#else const char *ue4_module_options = "linux_global_symbols"; #endif +#endif #include "Runtime/Core/Public/Misc/CommandLine.h" #include "Runtime/Core/Public/Misc/ConfigCacheIni.h" From b76e101442af6177adca89db9f56e5d3fe93ad62 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 4 Jul 2020 15:56:54 +0100 Subject: [PATCH 16/31] More wraps of 4.24 updates with appropriate ENGINE_MINOR_VERSION so still builds on 4.18. Builds on 4.18 and 4.25 (only versions tested). --- Source/PythonEditor/Private/PythonEditor.cpp | 4 +- .../Private/PythonProjectEditor.cpp | 6 ++ .../UEPyFMaterialEditorUtilities.cpp | 8 ++- .../UnrealEnginePython/Private/UEPyEditor.cpp | 20 ++++++ .../Private/UObject/UEPyMaterial.cpp | 13 ++++ .../Private/UObject/UEPySequencer.cpp | 30 ++++++++- .../Private/UObject/UEPyStaticMesh.cpp | 7 +++ .../Private/UObject/UEPyViewport.cpp | 62 +++++++++++++++++++ 8 files changed, 146 insertions(+), 4 deletions(-) diff --git a/Source/PythonEditor/Private/PythonEditor.cpp b/Source/PythonEditor/Private/PythonEditor.cpp index 00d8cb9f4..de74a2dac 100644 --- a/Source/PythonEditor/Private/PythonEditor.cpp +++ b/Source/PythonEditor/Private/PythonEditor.cpp @@ -7,7 +7,9 @@ #include "PythonEditorStyle.h" #include "PythonProjectEditor.h" #include "PythonProject.h" +#if ENGINE_MINOR_VERSION >= 24 #include "Subsystems/AssetEditorSubsystem.h" +#endif #include "Runtime/Slate/Public/Framework/MultiBox/MultiBoxBuilder.h" static const FName PythonEditorTabName( TEXT( "PythonEditor" ) ); @@ -91,4 +93,4 @@ class FPythonEditor : public IModuleInterface IMPLEMENT_MODULE( FPythonEditor, PythonEditor ) -#undef LOCTEXT_NAMESPACE \ No newline at end of file +#undef LOCTEXT_NAMESPACE diff --git a/Source/PythonEditor/Private/PythonProjectEditor.cpp b/Source/PythonEditor/Private/PythonProjectEditor.cpp index 1d17d5bf5..d5b90c6c5 100644 --- a/Source/PythonEditor/Private/PythonProjectEditor.cpp +++ b/Source/PythonEditor/Private/PythonProjectEditor.cpp @@ -2,9 +2,11 @@ #include "PythonProjectEditor.h" +#if ENGINE_MINOR_VERSION >= 24 #include "Subsystems/AssetEditorSubsystem.h" // #include "UnrealEd.h" +#endif #include "SPythonEditor.h" #include "SPythonProjectEditor.h" @@ -216,9 +218,13 @@ void FPythonProjectEditor::RegisterToolbarTab(const TSharedRef& InitToolkitHost, class UPythonProject* PythonProject) { +#if ENGINE_MINOR_VERSION >= 24 // UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); // GEditor->GetEditorSubsystem()->CloseOtherEditors(PythonProject, this); GEditor->GetEditorSubsystem()->CloseOtherEditors(PythonProject, this); +#else + FAssetEditorManager::Get().CloseOtherEditors(PythonProject, this); +#endif PythonProjectBeingEdited = PythonProject; TSharedPtr ThisPtr(SharedThis(this)); diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp index 0cd0bdf4d..3239c2f07 100644 --- a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp @@ -3,7 +3,9 @@ #if WITH_EDITOR +#if ENGINE_MINOR_VERSION >= 24 #include "Subsystems/AssetEditorSubsystem.h" +#endif #include "Materials/Material.h" #include "Runtime/Engine/Classes/EdGraph/EdGraph.h" @@ -57,9 +59,13 @@ static PyObject *py_ue_command_apply(PyObject *cls, PyObject * args) return PyErr_Format(PyExc_Exception, "argument is not a UMaterial"); } +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); IAssetEditorInstance *Instance = GEditor->GetEditorSubsystem()->FindEditorForAsset(Material, false); +#else + IAssetEditorInstance *Instance = FAssetEditorManager::Get().FindEditorForAsset(Material, false); +#endif if (!Instance) { return PyErr_Format(PyExc_Exception, "unable to retrieve editor for UMaterial"); @@ -133,4 +139,4 @@ void ue_python_init_fmaterial_editor_utilities(PyObject *ue_module) PyModule_AddObject(ue_module, "FMaterialEditorUtilities", (PyObject *)&ue_PyFMaterialEditorUtilitiesType); } -#endif \ No newline at end of file +#endif diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 08c518616..4e2a1147e 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -1178,8 +1178,12 @@ PyObject *py_unreal_engine_get_selected_assets(PyObject * self, PyObject * args) PyObject *py_unreal_engine_get_all_edited_assets(PyObject * self, PyObject * args) { +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); TArray assets = GEditor->GetEditorSubsystem()->GetAllEditedAssets(); +#else + TArray assets = FAssetEditorManager::Get().GetAllEditedAssets(); +#endif PyObject *assets_list = PyList_New(0); for (UObject *asset : assets) @@ -1207,9 +1211,13 @@ PyObject *py_unreal_engine_open_editor_for_asset(PyObject * self, PyObject * arg if (!u_obj) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); if (GEditor->GetEditorSubsystem()->OpenEditorForAsset(u_obj)) +#else + if (FAssetEditorManager::Get().OpenEditorForAsset(u_obj)) +#endif { Py_RETURN_TRUE; } @@ -1234,8 +1242,12 @@ PyObject *py_unreal_engine_find_editor_for_asset(PyObject * self, PyObject * arg if (py_bool && PyObject_IsTrue(py_bool)) bFocus = true; +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); IAssetEditorInstance *instance = GEditor->GetEditorSubsystem()->FindEditorForAsset(u_obj, bFocus); +#else + IAssetEditorInstance *instance = FAssetEditorManager::Get().FindEditorForAsset(u_obj, bFocus); +#endif if (!instance) return PyErr_Format(PyExc_Exception, "no editor found for asset"); @@ -1255,15 +1267,23 @@ PyObject *py_unreal_engine_close_editor_for_asset(PyObject * self, PyObject * ar if (!u_obj) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); GEditor->GetEditorSubsystem()->CloseAllEditorsForAsset(u_obj); +#else + FAssetEditorManager::Get().CloseAllEditorsForAsset(u_obj); +#endif Py_RETURN_NONE; } PyObject *py_unreal_engine_close_all_asset_editors(PyObject * self, PyObject * args) { +#if ENGINE_MINOR_VERSION >= 24 GEditor->GetEditorSubsystem()->CloseAllAssetEditors(); +#else + FAssetEditorManager::Get().CloseAllAssetEditors(); +#endif Py_RETURN_NONE; } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index efe80cc65..19d8653b6 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -511,7 +511,12 @@ PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * FMeshSectionInfo info = mesh->SectionInfoMap.Get(lod_index, material_index); #endif info.bEnableCollision = enabled; + +#if ENGINE_MINOR_VERSION >= 23 mesh->GetSectionInfoMap().Set(lod_index, material_index, info); +#else + mesh->SectionInfoMap.Set(lod_index, material_index, info); +#endif mesh->MarkPackageDirty(); @@ -545,9 +550,17 @@ PyObject *py_ue_static_mesh_set_shadow_for_lod(ue_PyUObject *self, PyObject * ar enabled = true; } +#if ENGINE_MINOR_VERSION >= 23 FMeshSectionInfo info = mesh->GetSectionInfoMap().Get(lod_index, material_index); +#else + FMeshSectionInfo info = mesh->SectionInfoMap.Get(lod_index, material_index); +#endif info.bCastShadow = enabled; +#if ENGINE_MINOR_VERSION >= 23 mesh->GetSectionInfoMap().Set(lod_index, material_index, info); +#else + mesh->SectionInfoMap.Set(lod_index, material_index, info); +#endif mesh->MarkPackageDirty(); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 50183917b..8d5e5bf3e 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -8,7 +8,9 @@ #include "Runtime/LevelSequence/Public/LevelSequence.h" #if WITH_EDITOR +#if ENGINE_MINOR_VERSION >= 24 #include "Subsystems/AssetEditorSubsystem.h" +#endif #include "Editor/Sequencer/Public/ISequencer.h" #include "Editor/Sequencer/Public/ISequencerModule.h" #include "Editor/UnrealEd/Public/Toolkits/AssetEditorManager.h" @@ -237,12 +239,18 @@ PyObject *py_ue_sequencer_changed(ue_PyUObject *self, PyObject * args) if (py_bool && PyObject_IsTrue(py_bool)) { // try to open the editor for the asset - // FAssetEditorManager::Get().OpenEditorForAsset(seq); +#if ENGINE_MINOR_VERSION >= 24 GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); +#else + FAssetEditorManager::Get().OpenEditorForAsset(seq); +#endif } - // IAssetEditorInstance* editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); +#if ENGINE_MINOR_VERSION >= 24 IAssetEditorInstance* editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(seq, true); +#else + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); +#endif if (editor) { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; @@ -474,10 +482,16 @@ PyObject *py_ue_sequencer_add_actor(ue_PyUObject *self, PyObject * args) // try to open the editor for the asset +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); IAssetEditorInstance *editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(seq, true); +#else + FAssetEditorManager::Get().OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); +#endif if (editor) { FLevelSequenceEditorToolkit *toolkit = (FLevelSequenceEditorToolkit *)editor; @@ -531,10 +545,16 @@ PyObject *py_ue_sequencer_add_actor_component(ue_PyUObject *self, PyObject * arg // try to open the editor for the asset +#if ENGINE_MINOR_VERSION >= 24 UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); AssetEditorSubsystem->OpenEditorForAsset(seq); IAssetEditorInstance *editor = AssetEditorSubsystem->FindEditorForAsset(seq, true); +#else + FAssetEditorManager::Get().OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); +#endif FGuid new_guid; if (editor) { @@ -582,10 +602,16 @@ PyObject *py_ue_sequencer_make_new_spawnable(ue_PyUObject *self, PyObject * args // try to open the editor for the asset +#if ENGINE_MINOR_VERSION >= 24 //UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem(); GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); IAssetEditorInstance *editor = GEditor->GetEditorSubsystem()->FindEditorForAsset(seq, true); +#else + FAssetEditorManager::Get().OpenEditorForAsset(seq); + + IAssetEditorInstance *editor = FAssetEditorManager::Get().FindEditorForAsset(seq, true); +#endif if (!editor) { return PyErr_Format(PyExc_Exception, "unable to access sequencer"); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp index 43b4cf434..8d49161ab 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp @@ -124,10 +124,17 @@ PyObject *py_ue_static_mesh_get_raw_mesh(ue_PyUObject *self, PyObject * args) FRawMesh raw_mesh; +#if ENGINE_MINOR_VERSION >= 24 if (lod_index < 0 || lod_index >= mesh->GetSourceModels().Num()) return PyErr_Format(PyExc_Exception, "invalid LOD index"); mesh->GetSourceModel(lod_index).RawMeshBulkData->LoadRawMesh(raw_mesh); +#else + if (lod_index < 0 || lod_index >= mesh->SourceModels.Num()) + return PyErr_Format(PyExc_Exception, "invalid LOD index"); + + mesh->SourceModels[lod_index].RawMeshBulkData->LoadRawMesh(raw_mesh); +#endif return py_ue_new_fraw_mesh(raw_mesh); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp index 1b2d1e5a0..ab9aafce2 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp @@ -61,15 +61,31 @@ PyObject *py_unreal_engine_editor_set_view_mode(PyObject * self, PyObject * args return NULL; } +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); +#else + FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); +#endif +#if ENGINE_MINOR_VERSION >= 24 if (!ActiveLevelViewport.IsValid()) +#else + if (!EditorModule.GetFirstActiveViewport().IsValid()) +#endif return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); +#else + FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); +#endif +#if ENGINE_MINOR_VERSION >= 24 LevelViewportClient.SetViewMode((EViewModeIndex)mode); +#else + viewport_client.SetViewMode((EViewModeIndex)mode); +#endif Py_RETURN_NONE; } @@ -85,15 +101,29 @@ PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * a } +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); if (!ActiveLevelViewport.IsValid()) +#else + FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + + if (!EditorModule.GetFirstActiveViewport().IsValid()) +#endif return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); +#else + FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); +#endif +#if ENGINE_MINOR_VERSION >= 24 LevelViewportClient.SetCameraSpeedSetting(speed); +#else + viewport_client.SetCameraSpeedSetting(speed); +#endif Py_RETURN_NONE; } @@ -112,15 +142,31 @@ PyObject *py_unreal_engine_editor_set_view_location(PyObject * self, PyObject * if (!vector) return PyErr_Format(PyExc_Exception, "argument is not a FVector"); +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); +#else + FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); +#endif +#if ENGINE_MINOR_VERSION >= 24 if (!ActiveLevelViewport.IsValid()) +#else + if (!EditorModule.GetFirstActiveViewport().IsValid()) +#endif return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); +#else + FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); +#endif +#if ENGINE_MINOR_VERSION >= 24 LevelViewportClient.SetViewLocation(vector->vec); +#else + viewport_client.SetViewLocation(vector->vec); +#endif Py_RETURN_NONE; } @@ -139,15 +185,31 @@ PyObject *py_unreal_engine_editor_set_view_rotation(PyObject * self, PyObject * if (!rotator) return PyErr_Format(PyExc_Exception, "argument is not a FRotator"); +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveLevelViewport(); +#else + FLevelEditorModule &EditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); +#endif +#if ENGINE_MINOR_VERSION >= 24 if (!ActiveLevelViewport.IsValid()) +#else + if (!EditorModule.GetFirstActiveViewport().IsValid()) +#endif return PyErr_Format(PyExc_Exception, "no active LevelEditor Viewport"); +#if ENGINE_MINOR_VERSION >= 24 FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); +#else + FLevelEditorViewportClient &viewport_client = EditorModule.GetFirstActiveViewport()->GetLevelViewportClient(); +#endif +#if ENGINE_MINOR_VERSION >= 24 LevelViewportClient.SetViewRotation(rotator->rot); +#else + viewport_client.SetViewRotation(rotator->rot); +#endif Py_RETURN_NONE; } From 4a7a3f42eaf31d1f5651780ea469e7285bce8f10 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 29 Jun 2020 08:19:56 +0100 Subject: [PATCH 17/31] Updated to use FProperty/FFieldClass instead of UProperty. Currently builds (4.18 and 4.25) and some examples seem to run OK. This requires some changes to the example scripts. Started from Support4.25 pull req but now highly modified. --- .../Private/PythonDelegate.cpp | 12 + .../Private/PythonFunction.cpp | 16 + .../Private/UEPyAssetUserData.cpp | 18 + .../UnrealEnginePython/Private/UEPyEngine.cpp | 2 + .../Private/UEPyFPropertiesImporter.cpp | 116 + .../Private/UEPyFPropertiesImporter.h | 32 + .../UnrealEnginePython/Private/UEPyModule.cpp | 1970 +++++++++++++++-- .../UnrealEnginePython/Private/UEPyModule.h | 10 +- .../Private/UEPySubclassing.cpp | 297 ++- .../Private/UEPyUScriptStruct.cpp | 89 +- .../Private/UEPyUScriptStruct.h | 6 +- .../Private/UObject/UEPyObject.cpp | 665 ++++++ .../Private/UObject/UEPyObject.h | 4 + .../Private/UObject/UEPyProperty.cpp | 216 ++ .../Private/UObject/UEPyProperty.h | 14 + .../Public/UnrealEnginePython.h | 72 + 16 files changed, 3355 insertions(+), 184 deletions(-) create mode 100644 Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.cpp create mode 100644 Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.h create mode 100644 Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp create mode 100644 Source/UnrealEnginePython/Private/UObject/UEPyProperty.h diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index e7e4c6699..ec5986259 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -37,10 +37,18 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms) py_args = PyTuple_New(signature->NumParms); Py_ssize_t argn = 0; +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator PArgs(signature); +#else TFieldIterator PArgs(signature); +#endif for (; PArgs && argn < signature->NumParms && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty *prop = *PArgs; +#else UProperty *prop = *PArgs; +#endif PyObject *arg = ue_py_convert_property(prop, (uint8 *)Parms, 0); if (!arg) { @@ -63,7 +71,11 @@ void UPythonDelegate::ProcessEvent(UFunction *function, void *Parms) // currently useless as events do not return a value /* if (signature_set) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty *return_property = signature->GetReturnProperty(); +#else UProperty *return_property = signature->GetReturnProperty(); +#endif if (return_property && signature->ReturnValueOffset != MAX_uint16) { if (!ue_py_convert_pyobject(ret, return_property, (uint8 *)Parms)) { UE_LOG(LogPython, Error, TEXT("Invalid return value type for delegate")); diff --git a/Source/UnrealEnginePython/Private/PythonFunction.cpp b/Source/UnrealEnginePython/Private/PythonFunction.cpp index efeb5b059..5b6c623ac 100644 --- a/Source/UnrealEnginePython/Private/PythonFunction.cpp +++ b/Source/UnrealEnginePython/Private/PythonFunction.cpp @@ -30,7 +30,11 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) // count the number of arguments Py_ssize_t argn = (Context && !is_static) ? 1 : 0; +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator IArgs(function); +#else TFieldIterator IArgs(function); +#endif for (; IArgs && ((IArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++IArgs) { argn++; } @@ -56,7 +60,11 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) // is it a blueprint call ? if (*Stack.Code == EX_EndFunctionParms) { +#if ENGINE_MINOR_VERSION >= 25 + for (FProperty *prop = (FProperty *)function->ChildProperties; prop; prop = (FProperty *)prop->Next) { +#else for (UProperty *prop = (UProperty *)function->Children; prop; prop = (UProperty *)prop->Next) { +#endif if (prop->PropertyFlags & CPF_ReturnParm) continue; if (!on_error) { @@ -75,7 +83,11 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) //UE_LOG(LogPython, Warning, TEXT("BLUEPRINT CALL")); frame = (uint8 *)FMemory_Alloca(function->PropertiesSize); FMemory::Memzero(frame, function->PropertiesSize); +#if ENGINE_MINOR_VERSION >= 25 + for (FProperty *prop = (FProperty *)function->ChildProperties; *Stack.Code != EX_EndFunctionParms; prop = (FProperty *)prop->Next) { +#else for (UProperty *prop = (UProperty *)function->Children; *Stack.Code != EX_EndFunctionParms; prop = (UProperty *)prop->Next) { +#endif Stack.Step(Stack.Object, prop->ContainerPtrToValuePtr(frame)); if (prop->PropertyFlags & CPF_ReturnParm) continue; @@ -107,7 +119,11 @@ void UPythonFunction::CallPythonCallable(FFrame& Stack, RESULT_DECL) } // get return value (if required) +#if ENGINE_MINOR_VERSION >= 25 + FProperty *return_property = function->GetReturnProperty(); +#else UProperty *return_property = function->GetReturnProperty(); +#endif if (return_property && function->ReturnValueOffset != MAX_uint16) { #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("FOUND RETURN VALUE")); diff --git a/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp b/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp index a43657401..6933bab76 100644 --- a/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp +++ b/Source/UnrealEnginePython/Private/UEPyAssetUserData.cpp @@ -8,13 +8,22 @@ PyObject *py_ue_asset_import_data(ue_PyUObject * self, PyObject * args) ue_py_check(self); UStruct *u_struct = (UStruct *)self->ue_object->GetClass(); +#if ENGINE_MINOR_VERSION >= 25 + FClassProperty *f_property = (FClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); + if (!f_property) +#else UClassProperty *u_property = (UClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); if (!u_property) +#endif { return PyErr_Format(PyExc_Exception, "UObject does not have asset import data."); } +#if ENGINE_MINOR_VERSION >= 25 + UAssetImportData *import_data = (UAssetImportData *)f_property->GetPropertyValue_InContainer(self->ue_object); +#else UAssetImportData *import_data = (UAssetImportData *)u_property->GetPropertyValue_InContainer(self->ue_object); +#endif FAssetImportInfo *import_info = &import_data->SourceData; PyObject *ret = PyList_New(import_info->SourceFiles.Num()); for (int i = 0; i < import_info->SourceFiles.Num(); i++) @@ -47,8 +56,13 @@ PyObject *py_ue_asset_import_data_set_sources(ue_PyUObject * self, PyObject * ar TArray filenames; UStruct *u_struct = (UStruct *)self->ue_object->GetClass(); +#if ENGINE_MINOR_VERSION >= 25 + FClassProperty *f_property = (FClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); + if (!f_property) +#else UClassProperty *u_property = (UClassProperty *)u_struct->FindPropertyByName(TEXT("AssetImportData")); if (!u_property) +#endif { return PyErr_Format(PyExc_Exception, "UObject does not have asset import data."); } @@ -79,7 +93,11 @@ PyObject *py_ue_asset_import_data_set_sources(ue_PyUObject * self, PyObject * ar } +#if ENGINE_MINOR_VERSION >= 25 + UAssetImportData *import_data = (UAssetImportData *)f_property->GetPropertyValue_InContainer(self->ue_object); +#else UAssetImportData *import_data = (UAssetImportData *)u_property->GetPropertyValue_InContainer(self->ue_object); +#endif FAssetImportInfo *import_info = &import_data->SourceData; TArray sources; diff --git a/Source/UnrealEnginePython/Private/UEPyEngine.cpp b/Source/UnrealEnginePython/Private/UEPyEngine.cpp index 3deeed577..e724935c6 100644 --- a/Source/UnrealEnginePython/Private/UEPyEngine.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEngine.cpp @@ -593,8 +593,10 @@ PyObject *py_unreal_engine_new_object(PyObject * self, PyObject * args) outer = ue_py_check_type(py_outer); if (!outer) return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + EXTRA_UE_LOG(LogPython, Warning, TEXT("object creation outer is %s"), *outer->GetName()); } + UObject *new_object = nullptr; Py_BEGIN_ALLOW_THREADS; new_object = NewObject(outer, obj_class, f_name, (EObjectFlags)flags); diff --git a/Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.cpp b/Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.cpp new file mode 100644 index 000000000..5c262e233 --- /dev/null +++ b/Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.cpp @@ -0,0 +1,116 @@ +#include "UEPyFPropertiesImporter.h" + + +#if ENGINE_MINOR_VERSION >= 25 + + +static PyObject *ue_PyFPropertiesImporter_getattro(ue_PyFPropertiesImporter *self, PyObject *attr_name) +{ + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer called")); + PyObject *py_attr = PyObject_GenericGetAttr((PyObject *)self, attr_name); + if (!py_attr) + { + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer attr OK")); + if (PyUnicodeOrString_Check(attr_name)) + { + const char *attr = UEPyUnicode_AsUTF8(attr_name); + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer attr is string %s"), UTF8_TO_TCHAR(attr)); + if (attr[0] != '_') + { + // dont know what to do here + // no - as far as I can see we cannot search for property classes anymore + // we have to explicitly add them + // - so do we need this - just import the explicit property wrap + // actually this would have to be the property class wrap + + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer field classes %d"), FFieldClass::GetAllFieldClasses().Num()); + //for (const FFieldClass* FieldClass : FFieldClass::GetAllFieldClasses()) + //{ + // EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer field classes %s"), *FieldClass->GetName()); + //} + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer field classes map %d"), FFieldClass::GetNameToFieldClassMap().Num()); + + // so apparently there is a map of all field classes and this is how we can look it up + FName FieldTypeStr = FName(UTF8_TO_TCHAR(attr)); + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer looking for %s"), *FieldTypeStr.ToString()); + FFieldClass** f_class_ptr = FFieldClass::GetNameToFieldClassMap().Find(FieldTypeStr); + if (f_class_ptr) + { + EXTRA_UE_LOG(LogPython, Warning, TEXT("magic properties importer found class")); + // swallow old exception + PyErr_Clear(); + // if we create python wrapper each time then think we dont want the extra inc ref + // - PyObject_New has already incremented new python objects ref count + // actually this may be OK - we only see this for import statements + // which in general are only done once - so this wrapper will last for lifetime + // of python script - except if we have multiple scripts we will get a different wrapper + // in each script + Py_RETURN_FFIELDCLASS_NOINC(*f_class_ptr); + } + + //UClass *u_class = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(attr)); + //if (u_class) + //{ + // // swallow old exception + // PyErr_Clear(); + // Py_RETURN_UOBJECT(u_class); + //} + + } + } + } + return py_attr; +} + +static PyTypeObject ue_PyFPropertiesImporterType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FPropertiesImporter", /* tp_name */ + sizeof(ue_PyFPropertiesImporter), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + (getattrofunc)ue_PyFPropertiesImporter_getattro, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine FProperties Importer", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, + 0, +}; + +void ue_python_init_fpropertiesimporter(PyObject *ue_module) +{ + ue_PyFPropertiesImporterType.tp_new = PyType_GenericNew; + + if (PyType_Ready(&ue_PyFPropertiesImporterType) < 0) + return; + + Py_INCREF(&ue_PyFPropertiesImporterType); + PyModule_AddObject(ue_module, "PropertiesImporter", (PyObject *)&ue_PyFPropertiesImporterType); +} + +PyObject *py_ue_new_fpropertiesimporter() +{ + ue_PyFPropertiesImporter *ret = (ue_PyFPropertiesImporter *)PyObject_New(ue_PyFPropertiesImporter, &ue_PyFPropertiesImporterType); + return (PyObject *)ret; +} + + +#endif diff --git a/Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.h b/Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.h new file mode 100644 index 000000000..2141955e8 --- /dev/null +++ b/Source/UnrealEnginePython/Private/UEPyFPropertiesImporter.h @@ -0,0 +1,32 @@ +#pragma once + + + +#include "UEPyModule.h" + +#if ENGINE_MINOR_VERSION >= 25 + +#include "Runtime/CoreUObject/Public/UObject/Field.h" + +// as properties no longer UObjects they wont get imported by the UObjects classes importer + +// - this does mean all scripts will need some editing +// - for the moment have to live with this until have some working properties + +// as now there is no longer any way I can see so far of getting a list of property classes +// defined we will have to have wrappers for each known property class +// WRONG - there is apparently a list of FFieldClass classes accessed either as +// FFieldClass::GetAllFieldClasses() or a TMap FFieldClass::GetNameToFieldClassMap() + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ +} ue_PyFPropertiesImporter; + +PyObject *py_ue_new_fpropertiesimporter(); + +void ue_python_init_fpropertiesimporter(PyObject *); + + +#endif diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index b2f2c9524..d3afb8ecf 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -6,6 +6,9 @@ #include "UEPyVisualLogger.h" #include "UObject/UEPyObject.h" +#if ENGINE_MINOR_VERSION >= 25 +#include "UObject/UEPyProperty.h" +#endif #include "UObject/UEPyActor.h" #include "UObject/UEPyTransform.h" #include "UObject/UEPyPlayer.h" @@ -38,6 +41,10 @@ #include "UObject/UEPyExporter.h" #include "UObject/UEPyFoliage.h" +#ifdef EXTRA_DEBUG_CODE +#include "Editor/BlueprintGraph/Classes/K2Node_DynamicCast.h" +#endif + #include "UEPyAssetUserData.h" #if WITH_EDITOR @@ -84,6 +91,10 @@ #include "UEPyEnumsImporter.h" #include "UEPyUStructsImporter.h" +#if ENGINE_MINOR_VERSION >= 25 +#include "UEPyFPropertiesImporter.h" +#endif + #include "UEPyUScriptStruct.h" #if WITH_EDITOR @@ -572,7 +583,11 @@ static PyMethodDef ue_PyUObject_methods[] = { { "properties", (PyCFunction)py_ue_properties, METH_VARARGS, "" }, { "get_property_class", (PyCFunction)py_ue_get_property_class, METH_VARARGS, "" }, { "has_property", (PyCFunction)py_ue_has_property, METH_VARARGS, "" }, +#if ENGINE_MINOR_VERSION >= 25 + { "get_fproperty", (PyCFunction)py_ue_get_fproperty, METH_VARARGS, "" }, +#else { "get_uproperty", (PyCFunction)py_ue_get_uproperty, METH_VARARGS, "" }, +#endif { "get_property_struct", (PyCFunction)py_ue_get_property_struct, METH_VARARGS, "" }, { "get_property_array_dim", (PyCFunction)py_ue_get_property_array_dim, METH_VARARGS, "" }, { "get_inner", (PyCFunction)py_ue_get_inner, METH_VARARGS, "" }, @@ -1218,6 +1233,7 @@ static PyObject* ue_PyUObject_getattro(ue_PyUObject* self, PyObject* attr_name) if (PyUnicodeOrString_Check(attr_name)) { const char* attr = UEPyUnicode_AsUTF8(attr_name); + EXTRA_UE_LOG(LogPython, Warning, TEXT("Getting attr %s"), UTF8_TO_TCHAR(attr)); // first check for property UStruct* u_struct = nullptr; if (self->ue_object->IsA()) @@ -1228,6 +1244,15 @@ static PyObject* ue_PyUObject_getattro(ue_PyUObject* self, PyObject* attr_name) { u_struct = (UStruct*)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty* f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); + if (f_property) + { + // swallow previous exception + PyErr_Clear(); + return ue_py_convert_property(f_property, (uint8*)self->ue_object, 0); + } +#else UProperty* u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); if (u_property) { @@ -1235,6 +1260,7 @@ static PyObject* ue_PyUObject_getattro(ue_PyUObject* self, PyObject* attr_name) PyErr_Clear(); return ue_py_convert_property(u_property, (uint8*)self->ue_object, 0); } +#endif UFunction* function = self->ue_object->FindFunction(FName(UTF8_TO_TCHAR(attr))); // retry wth K2_ prefix @@ -1320,10 +1346,11 @@ static int ue_PyUObject_setattro(ue_PyUObject* self, PyObject* attr_name, PyObje { ue_py_check_int(self); - // first of all check for UProperty + // first of all check for Property (UProperty or FProperty) if (PyUnicodeOrString_Check(attr_name)) { const char* attr = UEPyUnicode_AsUTF8(attr_name); + EXTRA_UE_LOG(LogPython, Warning, TEXT("Setting attr %s"), UTF8_TO_TCHAR(attr)); // first check for property UStruct* u_struct = nullptr; if (self->ue_object->IsA()) @@ -1334,6 +1361,61 @@ static int ue_PyUObject_setattro(ue_PyUObject* self, PyObject* attr_name, PyObje { u_struct = (UStruct*)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty* f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); + if (f_property) + { +#ifdef EXTRA_DEBUG_CODE + if (!strcmp(attr, "TargetType")) + { + ue_PyUObject* value_obj = (ue_PyUObject*)value; + UClass* u_class = (UClass*)(value_obj->ue_object); + EXTRA_UE_LOG(LogPython, Warning, TEXT("Setting attr %s targetype is %p"), UTF8_TO_TCHAR(attr), (void *)u_class); + if (self->ue_object->IsA()) + { + EXTRA_UE_LOG(LogPython, Warning, TEXT("node is dynamic cast")); + } + else + { + EXTRA_UE_LOG(LogPython, Warning, TEXT("node is NOT dynamic cast")); + } + } +#endif +#if WITH_EDITOR + self->ue_object->PreEditChange(f_property); +#endif + if (ue_py_convert_pyobject(value, f_property, (uint8*)self->ue_object, 0)) + { +#if WITH_EDITOR + FPropertyChangedEvent PropertyEvent(f_property, EPropertyChangeType::ValueSet); + self->ue_object->PostEditChangeProperty(PropertyEvent); + + if (self->ue_object->HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) + { + TArray Instances; + self->ue_object->GetArchetypeInstances(Instances); + for (UObject* Instance : Instances) + { + Instance->PreEditChange(f_property); + if (ue_py_convert_pyobject(value, f_property, (uint8*)Instance, 0)) + { + FPropertyChangedEvent InstancePropertyEvent(f_property, EPropertyChangeType::ValueSet); + Instance->PostEditChangeProperty(InstancePropertyEvent); + } + else + { + PyErr_SetString(PyExc_ValueError, "invalid value for FProperty"); + return -1; + } + } + } +#endif + return 0; + } + PyErr_SetString(PyExc_ValueError, "invalid value for FProperty"); + return -1; + } +#else UProperty* u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); if (u_property) { @@ -1371,6 +1453,7 @@ static int ue_PyUObject_setattro(ue_PyUObject* self, PyObject* attr_name, PyObje PyErr_SetString(PyExc_ValueError, "invalid value for UProperty"); return -1; } +#endif // now check for function name if (self->ue_object->FindFunction(FName(UTF8_TO_TCHAR(attr)))) @@ -1402,6 +1485,7 @@ static PyObject* ue_PyUObject_call(ue_PyUObject* self, PyObject* args, PyObject* if (self->ue_object->IsA()) { UClass* u_class = (UClass*)self->ue_object; + EXTRA_UE_LOG(LogPython, Warning, TEXT("Creating new UObject %s"), *u_class->GetName()); if (u_class->HasAnyClassFlags(CLASS_Abstract)) { return PyErr_Format(PyExc_Exception, "abstract classes cannot be instantiated"); @@ -1462,6 +1546,7 @@ static PyObject* ue_PyUObject_call(ue_PyUObject* self, PyObject* args, PyObject* if (self->ue_object->IsA()) { UScriptStruct* u_script_struct = (UScriptStruct*)self->ue_object; + EXTRA_UE_LOG(LogPython, Warning, TEXT("Creating new UScriptStruct %s"), *u_script_struct->GetName()); uint8* data = (uint8*)FMemory::Malloc(u_script_struct->GetStructureSize()); u_script_struct->InitializeStruct(data); #if WITH_EDITOR @@ -1497,6 +1582,22 @@ static PyObject* ue_PyUObject_call(ue_PyUObject* self, PyObject* args, PyObject* break; } +#if ENGINE_MINOR_VERSION >= 25 + FProperty* f_property = ue_struct_get_field_from_name(u_script_struct, (char*)struct_key); + if (f_property) + { + if (!ue_py_convert_pyobject(value, f_property, data, 0)) + { + FMemory::Free(data); + return PyErr_Format(PyExc_Exception, "invalid value for FProperty"); + } + } + else + { + FMemory::Free(data); + return PyErr_Format(PyExc_Exception, "FProperty %s not found", struct_key); + } +#else UProperty* u_property = ue_struct_get_field_from_name(u_script_struct, (char*)struct_key); if (u_property) { @@ -1511,6 +1612,7 @@ static PyObject* ue_PyUObject_call(ue_PyUObject* self, PyObject* args, PyObject* FMemory::Free(data); return PyErr_Format(PyExc_Exception, "UProperty %s not found", struct_key); } +#endif } } return py_ue_new_owned_uscriptstruct_zero_copy(u_script_struct, data); @@ -1551,6 +1653,227 @@ static PyTypeObject ue_PyUObjectType = { }; +#if ENGINE_MINOR_VERSION >= 25 + +static PyObject* ue_PyFProperty_call(ue_PyFProperty* self, PyObject* args, PyObject* kw) +{ + //ue_py_check(self); + // if it is a class, create a new object + //if (self->ue_object->IsA()) + //{ + // UClass* u_class = (UClass*)self->ue_object; + //} + return PyErr_Format(PyExc_Exception, "the specified fproperty has no __call__ support"); +} + +static PyObject* ue_PyFProperty_getattro(ue_PyFProperty* self, PyObject* attr_name) +{ + // this checks for a valid uobject - whats the FProperty equivalent?? + // minimally check if self->ue_fproperty is not NULL + //ue_py_check(self); + + const char* attr = "Invalid attribute name"; + if (PyUnicodeOrString_Check(attr_name)) + { + attr = UEPyUnicode_AsUTF8(attr_name); + } + EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_PyFProperty_getattro %s"), UTF8_TO_TCHAR(attr)); + + PyObject* ret = PyObject_GenericGetAttr((PyObject*)self, attr_name); + if (!ret) + { + } + return ret; +} + +static int ue_PyFProperty_setattro(ue_PyFProperty* self, PyObject* attr_name, PyObject* value) +{ + // this checks for a valid uobject - whats the FProperty equivalent?? + // minimally check if self->ue_fproperty is not NULL + //ue_py_check_int(self); + + // first of all check for Property (UProperty or FProperty) + const char* attr = "Invalid attribute name"; + if (PyUnicodeOrString_Check(attr_name)) + { + attr = UEPyUnicode_AsUTF8(attr_name); + } + EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_PyFProperty_setattro %s"), UTF8_TO_TCHAR(attr)); + + return -1; +} + + +static PyMethodDef ue_PyFProperty_methods[] = { + { "get_metadata", (PyCFunction)py_ue_fproperty_get_metadata, METH_VARARGS, "" }, + { "set_metadata", (PyCFunction)py_ue_fproperty_set_metadata, METH_VARARGS, "" }, + { "has_metadata", (PyCFunction)py_ue_fproperty_has_metadata, METH_VARARGS, "" }, + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyFProperty_str(ue_PyFProperty *self) +{ + return PyUnicode_FromFormat("", + self->ue_fproperty); +} + +static void ue_PyFProperty_dealloc(ue_PyFProperty *self) +{ + delete(self->ue_fproperty); + EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_PyFProperty_dealloc")); +#if PY_MAJOR_VERSION < 3 + self->ob_type->tp_free((PyObject*)self); +#else + Py_TYPE(self)->tp_free((PyObject*)self); +#endif +} + + + + +static PyTypeObject ue_PyFPropertyType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FProperty", /* tp_name */ + sizeof(ue_PyFProperty), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ue_PyFProperty_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)ue_PyFProperty_call, /* tp_call */ + (reprfunc)ue_PyFProperty_str, /* tp_str */ + (getattrofunc)ue_PyFProperty_getattro, /* tp_getattro */ + (setattrofunc)ue_PyFProperty_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine FProperty wrapper", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFProperty_methods, /* tp_methods */ +}; + + +// so for FProperties we need a separate wrap of the class as this is a different +// c++ type - for UObjects the class object UClass is also a UObject + +static PyObject* ue_PyFFieldClass_call(ue_PyFFieldClass* self, PyObject* args, PyObject* kw) +{ + //ue_py_check(self); + // if it is a class, create a new object + //if (self->ue_object->IsA()) + //{ + // UClass* u_class = (UClass*)self->ue_object; + //} + return PyErr_Format(PyExc_Exception, "the specified ffieldclass has no __call__ support"); +} + +static PyObject* ue_PyFFieldClass_getattro(ue_PyFFieldClass* self, PyObject* attr_name) +{ + // this checks for a valid uobject - whats the FFieldClass equivalent?? + // minimally check if self->ue_fproperty is not NULL + //ue_py_check(self); + + const char* attr = "Invalid attribute name"; + if (PyUnicodeOrString_Check(attr_name)) + { + attr = UEPyUnicode_AsUTF8(attr_name); + } + EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_PyFFieldClass_getattro %s"), UTF8_TO_TCHAR(attr)); + + PyObject* ret = PyObject_GenericGetAttr((PyObject*)self, attr_name); + if (!ret) + { + } + + return ret; +} + +static int ue_PyFFieldClass_setattro(ue_PyFFieldClass* self, PyObject* attr_name, PyObject* value) +{ + // this checks for a valid uobject - whats the FFieldClass equivalent?? + // minimally check if self->ue_fproperty is not NULL + //ue_py_check_int(self); + + // first of all check for Property (UProperty or FProperty) + const char* attr = "Invalid attribute name"; + if (PyUnicodeOrString_Check(attr_name)) + { + attr = UEPyUnicode_AsUTF8(attr_name); + } + EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_PyFFieldClass_setattro %s"), UTF8_TO_TCHAR(attr)); + + return -1; +} + + +static PyMethodDef ue_PyFFieldClass_methods[] = { + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyFFieldClass_str(ue_PyFFieldClass *self) +{ + return PyUnicode_FromFormat("", + self->ue_ffieldclass); +} + +static void ue_PyFFieldClass_dealloc(ue_PyFFieldClass *self) +{ + delete(self->ue_ffieldclass); + EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_PyFFieldClass_dealloc")); +#if PY_MAJOR_VERSION < 3 + self->ob_type->tp_free((PyObject*)self); +#else + Py_TYPE(self)->tp_free((PyObject*)self); +#endif +} + + + + +static PyTypeObject ue_PyFFieldClassType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FFieldClass", /* tp_name */ + sizeof(ue_PyFFieldClass), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ue_PyFFieldClass_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)ue_PyFFieldClass_call, /* tp_call */ + (reprfunc)ue_PyFFieldClass_str, /* tp_str */ + (getattrofunc)ue_PyFFieldClass_getattro, /* tp_getattro */ + (setattrofunc)ue_PyFFieldClass_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Unreal Engine FFieldClass wrapper", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFFieldClass_methods, /* tp_methods */ +}; + + +#endif + @@ -1583,6 +1906,9 @@ UClass* unreal_engine_new_uclass(char* name, UClass* outer_parent) if (is_overwriting && new_object->Children) { + // would like to know the exact semantics of this loop + // but it looks like its just removing function objects in the Children list + // so this code should still work for 4.25 UField* u_field = new_object->Children; while (u_field) { @@ -1598,6 +1924,14 @@ UClass* unreal_engine_new_uclass(char* name, UClass* outer_parent) new_object->PurgeClass(true); new_object->Children = nullptr; new_object->ClassAddReferencedObjects = parent->ClassAddReferencedObjects; + // NOTA BENE we may need to do something with ChildProperties now + // as apparently the previous Children list now split into 2 lists + // with properties in ChildProperties + // in fact given that the above nulled the entire list it would suggest + // the following is needed +#if ENGINE_MINOR_VERSION >= 25 + new_object->ChildProperties = nullptr; +#endif } new_object->PropertiesSize = 0; @@ -1660,6 +1994,10 @@ UClass* unreal_engine_new_uclass(char* name, UClass* outer_parent) int unreal_engine_py_init(ue_PyUObject*, PyObject*, PyObject*); +#if ENGINE_MINOR_VERSION >= 25 +int unreal_engine_py_fproperty_init(ue_PyUObject*, PyObject*, PyObject*); +int unreal_engine_py_ffieldclass_init(ue_PyUObject*, PyObject*, PyObject*); +#endif void unreal_engine_init_py_module() { @@ -1668,6 +2006,7 @@ void unreal_engine_init_py_module() #else PyObject* new_unreal_engine_module = Py_InitModule3("unreal_engine", NULL, unreal_engine_py_doc); #endif + ue_PyUObjectType.tp_new = PyType_GenericNew; ue_PyUObjectType.tp_init = (initproc)unreal_engine_py_init; ue_PyUObjectType.tp_dictoffset = offsetof(ue_PyUObject, py_dict); @@ -1678,6 +2017,32 @@ void unreal_engine_init_py_module() Py_INCREF(&ue_PyUObjectType); PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject*)& ue_PyUObjectType); +#if ENGINE_MINOR_VERSION >= 25 + ue_PyFPropertyType.tp_new = PyType_GenericNew; + ue_PyFPropertyType.tp_init = (initproc)unreal_engine_py_fproperty_init; + ue_PyFPropertyType.tp_dictoffset = offsetof(ue_PyFProperty, py_dict); + + if (PyType_Ready(&ue_PyFPropertyType) < 0) + return; + + Py_INCREF(&ue_PyFPropertyType); + PyModule_AddObject(new_unreal_engine_module, "FProperty", (PyObject*)& ue_PyFPropertyType); + + //EXTRA_UE_LOG(LogPython, Warning, TEXT("offset of tp_name is %d %x"), offsetof(PyTypeObject, tp_name), offsetof(PyTypeObject, tp_name)); + + ue_PyFFieldClassType.tp_new = PyType_GenericNew; + ue_PyFFieldClassType.tp_init = (initproc)unreal_engine_py_ffieldclass_init; + ue_PyFFieldClassType.tp_dictoffset = offsetof(ue_PyFFieldClass, py_dict); + + if (PyType_Ready(&ue_PyFFieldClassType) < 0) + return; + + Py_INCREF(&ue_PyFFieldClassType); + PyModule_AddObject(new_unreal_engine_module, "FFieldClass", (PyObject*)& ue_PyFFieldClassType); + + //EXTRA_UE_LOG(LogPython, Warning, TEXT("offset of tp_name is %d %x"), offsetof(PyTypeObject, tp_name), offsetof(PyTypeObject, tp_name)); +#endif + PyObject* unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); PyMethodDef* unreal_engine_function; @@ -1774,6 +2139,9 @@ void unreal_engine_init_py_module() ue_py_register_magic_module((char*)"unreal_engine.classes", py_ue_new_uclassesimporter); ue_py_register_magic_module((char*)"unreal_engine.enums", py_ue_new_enumsimporter); ue_py_register_magic_module((char*)"unreal_engine.structs", py_ue_new_ustructsimporter); +#if ENGINE_MINOR_VERSION >= 25 + ue_py_register_magic_module((char*)"unreal_engine.properties", py_ue_new_fpropertiesimporter); +#endif PyDict_SetItemString(unreal_engine_dict, "ENGINE_MAJOR_VERSION", PyLong_FromLong(ENGINE_MAJOR_VERSION)); @@ -1920,54 +2288,153 @@ ue_PyUObject* ue_get_python_uobject_inc(UObject* ue_obj) return ret; } -void unreal_engine_py_log_error() +#if ENGINE_MINOR_VERSION >= 25 +// so for the moment lets reimplement this using fproperty +ue_PyFProperty* ue_get_python_fproperty(FProperty* ue_fprop) { - PyObject* type = NULL; - PyObject* value = NULL; - PyObject* traceback = NULL; + if (!ue_fprop) + return nullptr; - PyErr_Fetch(&type, &value, &traceback); - PyErr_NormalizeException(&type, &value, &traceback); + // so Im still confused - the argument was a UObject class - if (!value) - { - PyErr_Clear(); - return; - } + // so for UObjects we store them in the housekeeping object + // which we lookup here - what to do?? - char* msg = NULL; -#if PY_MAJOR_VERSION >= 3 - PyObject * zero = PyUnicode_AsUTF8String(PyObject_Str(value)); - if (zero) - { - msg = PyBytes_AsString(zero); - } -#else - msg = PyString_AsString(PyObject_Str(value)); -#endif - if (!msg) - { - PyErr_Clear(); - return; - } + //ue_PyUObject* ret = FUnrealEnginePythonHouseKeeper::Get()->GetPyUObject(ue_obj); - UE_LOG(LogPython, Error, TEXT("%s"), UTF8_TO_TCHAR(msg)); + ue_PyFProperty* ret = nullptr; - // taken from uWSGI ;) - if (!traceback) + if (!ret) { - PyErr_Clear(); - return; - } - PyObject* traceback_module = PyImport_ImportModule("traceback"); - if (!traceback_module) + ue_PyFProperty* ue_py_property = (ue_PyFProperty*)PyObject_New(ue_PyFProperty, &ue_PyFPropertyType); + if (!ue_py_property) + { + return nullptr; + } + // so we must initialize the type struct variables + ue_py_property->ue_fproperty = ue_fprop; + //ue_py_property->py_proxy = nullptr; + ue_py_property->py_dict = PyDict_New(); + //ue_py_property->auto_rooted = 0; + //ue_py_property->owned = 0; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("CREATED UPyFProperty at %p for %p %s"), ue_py_property, ue_fprop, *ue_fprop->GetName()); +#endif + return ue_py_property; + } + return ret; +} + +ue_PyFProperty* ue_get_python_fproperty_inc(FProperty* ue_fprop) +{ + ue_PyFProperty* ret = ue_get_python_fproperty(ue_fprop); + if (ret) { - PyErr_Clear(); - return; + Py_INCREF(ret); } + return ret; +} - PyObject* traceback_dict = PyModule_GetDict(traceback_module); + +ue_PyFFieldClass* ue_get_python_ffieldclass(FFieldClass* ue_fclass) +{ + if (!ue_fclass) + return nullptr; + + // so Im still confused - the argument was a UObject class + + // so for UObjects we store them in the housekeeping object + // which we lookup here - what to do?? + + //ue_PyUObject* ret = FUnrealEnginePythonHouseKeeper::Get()->GetPyUObject(ue_obj); + + ue_PyFFieldClass* ret = nullptr; + + if (!ret) + { + + ue_PyFFieldClass* ue_py_fieldclass = (ue_PyFFieldClass*)PyObject_New(ue_PyFFieldClass, &ue_PyFFieldClassType); + if (!ue_py_fieldclass) + { + return nullptr; + } + // so we must initialize the type struct variables + ue_py_fieldclass->ue_ffieldclass = ue_fclass; + //ue_py_fieldclass->py_proxy = nullptr; + ue_py_fieldclass->py_dict = PyDict_New(); + //ue_py_fieldclass->auto_rooted = 0; + //ue_py_fieldclass->owned = 0; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("CREATED UPyFFieldClass at %p for %p %s"), ue_py_fieldclass, ue_fclass, *ue_fclass->GetName()); +#endif + EXTRA_UE_LOG(LogPython, Warning, TEXT("CREATED UPyFFieldClass at %p for %p %s"), ue_py_fieldclass, ue_fclass, *ue_fclass->GetName()); + return ue_py_fieldclass; + } + return ret; +} + +ue_PyFFieldClass* ue_get_python_ffieldclass_inc(FFieldClass* ue_fclass) +{ + // dont know if we need this because currently creating python wrapper each time + ue_PyFFieldClass* ret = ue_get_python_ffieldclass(ue_fclass); + if (ret) + { + Py_INCREF(ret); + } + return ret; +} + +#endif + +void unreal_engine_py_log_error() +{ + PyObject* type = NULL; + PyObject* value = NULL; + PyObject* traceback = NULL; + + PyErr_Fetch(&type, &value, &traceback); + PyErr_NormalizeException(&type, &value, &traceback); + + if (!value) + { + PyErr_Clear(); + return; + } + + char* msg = NULL; +#if PY_MAJOR_VERSION >= 3 + PyObject * zero = PyUnicode_AsUTF8String(PyObject_Str(value)); + if (zero) + { + msg = PyBytes_AsString(zero); + } +#else + msg = PyString_AsString(PyObject_Str(value)); +#endif + if (!msg) + { + PyErr_Clear(); + return; + } + + UE_LOG(LogPython, Error, TEXT("%s"), UTF8_TO_TCHAR(msg)); + + // taken from uWSGI ;) + if (!traceback) + { + PyErr_Clear(); + return; + } + + PyObject* traceback_module = PyImport_ImportModule("traceback"); + if (!traceback_module) + { + PyErr_Clear(); + return; + } + + PyObject* traceback_dict = PyModule_GetDict(traceback_module); PyObject* format_exception = PyDict_GetItemString(traceback_dict, "format_exception"); if (format_exception) @@ -2038,10 +2505,11 @@ AActor* ue_get_actor(ue_PyUObject* py_obj) return nullptr; } +#if ENGINE_MINOR_VERSION >= 25 // convert a property to a python object -PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) +PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { bool value = casted_prop->GetPropertyValue_InContainer(buffer, index); if (value) @@ -2051,70 +2519,69 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) Py_RETURN_FALSE; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { int value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyLong_FromLong(value); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { uint32 value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyLong_FromUnsignedLong(value); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { long long value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyLong_FromLongLong(value); } - if (auto casted_prop = Cast(prop)) + // this is likely a bug - it was a FInt64Property before + if (auto casted_prop = CastField(prop)) { uint64 value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyLong_FromUnsignedLongLong(value); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { float value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyFloat_FromDouble(value); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { uint8 value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyLong_FromUnsignedLong(value); } -#if ENGINE_MINOR_VERSION >= 15 - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); uint64 enum_index = casted_prop->GetUnderlyingProperty()->GetUnsignedIntPropertyValue(prop_addr); return PyLong_FromUnsignedLong(enum_index); } -#endif - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FString value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyUnicode_FromString(TCHAR_TO_UTF8(*value)); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FText value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyUnicode_FromString(TCHAR_TO_UTF8(*value.ToString())); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FName value = casted_prop->GetPropertyValue_InContainer(buffer, index); return PyUnicode_FromString(TCHAR_TO_UTF8(*value.ToString())); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { auto value = casted_prop->GetObjectPropertyValue_InContainer(buffer, index); if (value) @@ -2124,7 +2591,7 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) Py_RETURN_NONE; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { auto value = casted_prop->GetPropertyValue_InContainer(buffer, index); if (value) @@ -2135,7 +2602,7 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) } // try to manage known struct first - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (auto casted_struct = Cast(casted_prop->Struct)) { @@ -2179,7 +2646,7 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) return PyErr_Format(PyExc_TypeError, "unsupported UStruct type"); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { auto value = casted_prop->GetPropertyValue_InContainer(buffer, index); UObject* strong_obj = value.Get(); @@ -2191,24 +2658,24 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) Py_RETURN_NONE; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { - Py_RETURN_UOBJECT(casted_prop); + Py_RETURN_FPROPERTY(casted_prop); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { - Py_RETURN_UOBJECT(casted_prop); + Py_RETURN_FPROPERTY(casted_prop); } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer array_helper(casted_prop, buffer, index); - UProperty* array_prop = casted_prop->Inner; + FProperty* array_prop = casted_prop->Inner; // check for TArray, so we can use bytearray optimization - if (auto uint8_tarray = Cast(array_prop)) + if (auto uint8_tarray = CastField(array_prop)) { uint8* buf = array_helper.GetRawPtr(); return PyByteArray_FromStringAndSize((char*)buf, array_helper.Num()); @@ -2231,8 +2698,7 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) return py_list; } -#if ENGINE_MINOR_VERSION >= 15 - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); @@ -2267,18 +2733,17 @@ PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) return py_dict; } -#endif return PyErr_Format(PyExc_Exception, "unsupported value type %s for property %s", TCHAR_TO_UTF8(*prop->GetClass()->GetName()), TCHAR_TO_UTF8(*prop->GetName())); } // convert a python object to a property -bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, int32 index) +bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, int32 index) { if (PyBool_Check(py_obj)) { - auto casted_prop = Cast(prop); + auto casted_prop = CastField(prop); if (!casted_prop) return false; if (PyObject_IsTrue(py_obj)) @@ -2294,50 +2759,49 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (PyNumber_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLong(py_long), index); Py_DECREF(py_long); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); Py_DECREF(py_long); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLongLong(py_long), index); Py_DECREF(py_long); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLongLong(py_long), index); Py_DECREF(py_long); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_float = PyNumber_Float(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyFloat_AsDouble(py_float), index); Py_DECREF(py_float); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); Py_DECREF(py_long); return true; } -#if ENGINE_MINOR_VERSION >= 15 - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); @@ -2345,25 +2809,23 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in Py_DECREF(py_long); return true; } -#endif - return false; } if (PyUnicodeOrString_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj)), index); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj)), index); return true; } - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, FText::FromString(UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj))), index); return true; @@ -2373,11 +2835,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (PyBytes_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); - if (auto item_casted_prop = Cast(casted_prop->Inner)) + if (auto item_casted_prop = CastField(casted_prop->Inner)) { Py_ssize_t pybytes_len = PyBytes_Size(py_obj); @@ -2405,11 +2867,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (PyByteArray_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); - if (auto item_casted_prop = Cast(casted_prop->Inner)) + if (auto item_casted_prop = CastField(casted_prop->Inner)) { Py_ssize_t pybytes_len = PyByteArray_Size(py_obj); @@ -2438,11 +2900,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (PyList_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); - UProperty* array_prop = casted_prop->Inner; + FProperty* array_prop = casted_prop->Inner; Py_ssize_t pylist_len = PyList_Size(py_obj); // fix array helper size @@ -2471,11 +2933,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (PyTuple_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); - UProperty* array_prop = casted_prop->Inner; + FProperty* array_prop = casted_prop->Inner; Py_ssize_t pytuple_len = PyTuple_Size(py_obj); // fix array helper size @@ -2502,10 +2964,9 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return false; } -#if ENGINE_MINOR_VERSION >= 15 if (PyDict_Check(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); @@ -2537,13 +2998,12 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return false; } -#endif // structs if (ue_PyFVector * py_vec = py_ue_is_fvector(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) { @@ -2556,7 +3016,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_PyFVector2D * py_vec = py_ue_is_fvector2d(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) { @@ -2569,7 +3029,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_PyFRotator * py_rot = py_ue_is_frotator(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) { @@ -2582,7 +3042,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_PyFTransform * py_transform = py_ue_is_ftransform(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) { @@ -2595,7 +3055,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_PyFColor * py_color = py_ue_is_fcolor(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) { @@ -2609,7 +3069,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_PyFLinearColor * py_color = py_ue_is_flinearcolor(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) { @@ -2622,7 +3082,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_PyFHitResult * py_hit = py_ue_is_fhitresult(py_obj)) { - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == FHitResult::StaticStruct()) { @@ -2637,7 +3097,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (py_ue_is_uscriptstruct(py_obj)) { ue_PyUScriptStruct* py_u_struct = (ue_PyUScriptStruct*)py_obj; - if (auto casted_prop = Cast(prop)) + if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == py_u_struct->u_struct) { @@ -2650,41 +3110,51 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return false; } + EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 1")); + if (PyObject_IsInstance(py_obj, (PyObject*)& ue_PyUObjectType)) { ue_PyUObject* ue_obj = (ue_PyUObject*)py_obj; + EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 2 is uobject %s"), *ue_obj->ue_object->GetName()); if (ue_obj->ue_object->IsA()) { - if (auto casted_prop = Cast(prop)) + EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 3 is class %s"), *ue_obj->ue_object->GetName()); + if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, ue_obj->ue_object, index); + EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 3a is uclass %s"), *ue_obj->ue_object->GetName()); + UK2Node_DynamicCast* node = (UK2Node_DynamicCast*)buffer; + EXTRA_UE_LOG(LogPython, Warning, TEXT("Setting attr targetype is %p"), (void *)(node->TargetType)); return true; } - else if (auto casted_prop_soft_class = Cast(prop)) + else if (auto casted_prop_soft_class = CastField(prop)) { casted_prop_soft_class->SetPropertyValue_InContainer(buffer, FSoftObjectPtr(ue_obj->ue_object), index); return true; } - else if (auto casted_prop_soft_object = Cast(prop)) + else if (auto casted_prop_soft_object = CastField(prop)) { casted_prop_soft_object->SetPropertyValue_InContainer(buffer, FSoftObjectPtr(ue_obj->ue_object), index); return true; } - else if (auto casted_prop_weak_object = Cast(prop)) + else if (auto casted_prop_weak_object = CastField(prop)) { casted_prop_weak_object->SetPropertyValue_InContainer(buffer, FWeakObjectPtr(ue_obj->ue_object), index); return true; } - else if (auto casted_prop_base = Cast(prop)) + else if (auto casted_prop_base = CastField(prop)) { // ensure the object type is correct, otherwise crash could happen (soon or later) if (!ue_obj->ue_object->IsA(casted_prop_base->PropertyClass)) return false; + EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 3d is uobject %s"), *ue_obj->ue_object->GetName()); + // (UObject *)buffer + casted_prop_base->SetObjectPropertyValue_InContainer(buffer, ue_obj->ue_object, index); return true; @@ -2696,7 +3166,8 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (ue_obj->ue_object->IsA()) { - if (auto casted_prop = Cast(prop)) + EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 4 is uobject %s"), *ue_obj->ue_object->GetName()); + if (auto casted_prop = CastField(prop)) { // if the property specifies an interface, the object must be of a class that implements it if (casted_prop->PropertyClass->HasAnyClassFlags(CLASS_Interface)) @@ -2715,7 +3186,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return true; } - else if (auto casted_prop_soft_object = Cast(prop)) + else if (auto casted_prop_soft_object = CastField(prop)) { if (!ue_obj->ue_object->IsA(casted_prop_soft_object->PropertyClass)) return false; @@ -2724,7 +3195,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return true; } - else if (auto casted_prop_interface = Cast(prop)) + else if (auto casted_prop_interface = CastField(prop)) { // ensure the object type is correct, otherwise crash could happen (soon or later) if (!ue_obj->ue_object->GetClass()->ImplementsInterface(casted_prop_interface->InterfaceClass)) @@ -2740,7 +3211,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in if (py_obj == Py_None) { - auto casted_prop_class = Cast(prop); + auto casted_prop_class = CastField(prop); if (casted_prop_class) { @@ -2748,7 +3219,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return true; } - auto casted_prop = Cast(prop); + auto casted_prop = CastField(prop); if (casted_prop) { @@ -2762,84 +3233,831 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return false; } +#else +// convert a property to a python object +PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) +{ + if (auto casted_prop = Cast(prop)) + { + bool value = casted_prop->GetPropertyValue_InContainer(buffer, index); + if (value) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; + } + if (auto casted_prop = Cast(prop)) + { + int value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromLong(value); + } -// check if a python object is a wrapper to a UObject -ue_PyUObject* ue_is_pyuobject(PyObject* obj) -{ - if (!PyObject_IsInstance(obj, (PyObject*)& ue_PyUObjectType)) - return nullptr; - return (ue_PyUObject*)obj; -} + if (auto casted_prop = Cast(prop)) + { + uint32 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromUnsignedLong(value); + } -void ue_bind_events_for_py_class_by_attribute(UObject* u_obj, PyObject* py_class) -{ - // attempt to register events - PyObject* attrs = PyObject_Dir(py_class); - if (!attrs) - return; + if (auto casted_prop = Cast(prop)) + { + long long value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromLongLong(value); + } - AActor* actor = Cast(u_obj); - if (!actor) + if (auto casted_prop = Cast(prop)) { - UActorComponent* component = Cast(u_obj); - if (!component) - return; - actor = component->GetOwner(); + uint64 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromUnsignedLongLong(value); } - Py_ssize_t len = PyList_Size(attrs); - for (Py_ssize_t i = 0; i < len; i++) + if (auto casted_prop = Cast(prop)) { - PyObject* py_attr_name = PyList_GetItem(attrs, i); - if (!py_attr_name || !PyUnicodeOrString_Check(py_attr_name)) - continue; - PyObject* item = PyObject_GetAttrString(py_class, UEPyUnicode_AsUTF8(py_attr_name)); - if (item && PyCallable_Check(item)) - { - // check for ue_event signature - PyObject* event_signature = PyObject_GetAttrString(item, (char*)"ue_event"); - if (event_signature) - { - if (PyUnicodeOrString_Check(event_signature)) - { - FString event_name = FString(UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(event_signature))); - TArray parts; - int n = event_name.ParseIntoArray(parts, UTF8_TO_TCHAR(".")); - if (n < 1 || n > 2) - { - PyErr_SetString(PyExc_Exception, "invalid ue_event syntax, must be the name of an event or ComponentName.Event"); - unreal_engine_py_log_error(); - } - else - { - if (n == 1) - { - if (!ue_bind_pyevent(ue_get_python_uobject(actor), parts[0], item, true)) - { - unreal_engine_py_log_error(); - } - } - else - { - bool found = false; - for (UActorComponent* component : actor->GetComponents()) - { - if (component->GetFName() == FName(*parts[0])) - { - if (!ue_bind_pyevent(ue_get_python_uobject(component), parts[1], item, true)) - { - unreal_engine_py_log_error(); - } - found = true; - break; - } - } + float value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyFloat_FromDouble(value); + } - if (!found) - { - PyErr_SetString(PyExc_Exception, "unable to find component by name"); - unreal_engine_py_log_error(); + if (auto casted_prop = Cast(prop)) + { + uint8 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromUnsignedLong(value); + } + +#if ENGINE_MINOR_VERSION >= 15 + if (auto casted_prop = Cast(prop)) + { + void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); + uint64 enum_index = casted_prop->GetUnderlyingProperty()->GetUnsignedIntPropertyValue(prop_addr); + return PyLong_FromUnsignedLong(enum_index); + } +#endif + + if (auto casted_prop = Cast(prop)) + { + FString value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyUnicode_FromString(TCHAR_TO_UTF8(*value)); + } + + if (auto casted_prop = Cast(prop)) + { + FText value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyUnicode_FromString(TCHAR_TO_UTF8(*value.ToString())); + } + + if (auto casted_prop = Cast(prop)) + { + FName value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyUnicode_FromString(TCHAR_TO_UTF8(*value.ToString())); + } + + if (auto casted_prop = Cast(prop)) + { + auto value = casted_prop->GetObjectPropertyValue_InContainer(buffer, index); + if (value) + { + Py_RETURN_UOBJECT(value); + } + Py_RETURN_NONE; + } + + if (auto casted_prop = Cast(prop)) + { + auto value = casted_prop->GetPropertyValue_InContainer(buffer, index); + if (value) + { + Py_RETURN_UOBJECT(value); + } + return PyErr_Format(PyExc_Exception, "invalid UClass type for %s", TCHAR_TO_UTF8(*casted_prop->GetName())); + } + + // try to manage known struct first + if (auto casted_prop = Cast(prop)) + { + if (auto casted_struct = Cast(casted_prop->Struct)) + { + if (casted_struct == TBaseStructure::Get()) + { + FVector vec = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_fvector(vec); + } + if (casted_struct == TBaseStructure::Get()) + { + FVector2D vec = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_fvector2d(vec); + } + if (casted_struct == TBaseStructure::Get()) + { + FRotator rot = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_frotator(rot); + } + if (casted_struct == TBaseStructure::Get()) + { + FTransform transform = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_ftransform(transform); + } + if (casted_struct == FHitResult::StaticStruct()) + { + FHitResult hit = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_fhitresult(hit); + } + if (casted_struct == TBaseStructure::Get()) + { + FColor color = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_fcolor(color); + } + if (casted_struct == TBaseStructure::Get()) + { + FLinearColor color = *casted_prop->ContainerPtrToValuePtr(buffer, index); + return py_ue_new_flinearcolor(color); + } + return py_ue_new_uscriptstruct(casted_struct, casted_prop->ContainerPtrToValuePtr(buffer, index)); + } + return PyErr_Format(PyExc_TypeError, "unsupported UStruct type"); + } + + if (auto casted_prop = Cast(prop)) + { + auto value = casted_prop->GetPropertyValue_InContainer(buffer, index); + UObject* strong_obj = value.Get(); + if (strong_obj) + { + Py_RETURN_UOBJECT(strong_obj); + } + // nullptr + Py_RETURN_NONE; + } + + if (auto casted_prop = Cast(prop)) + { + Py_RETURN_UOBJECT(casted_prop); + } + + if (auto casted_prop = Cast(prop)) + { + Py_RETURN_UOBJECT(casted_prop); + } + + if (auto casted_prop = Cast(prop)) + { + FScriptArrayHelper_InContainer array_helper(casted_prop, buffer, index); + + UProperty* array_prop = casted_prop->Inner; + + // check for TArray, so we can use bytearray optimization + if (auto uint8_tarray = Cast(array_prop)) + { + uint8* buf = array_helper.GetRawPtr(); + return PyByteArray_FromStringAndSize((char*)buf, array_helper.Num()); + } + + PyObject* py_list = PyList_New(0); + + for (int i = 0; i < array_helper.Num(); i++) + { + PyObject* item = ue_py_convert_property(array_prop, array_helper.GetRawPtr(i), 0); + if (!item) + { + Py_DECREF(py_list); + return NULL; + } + PyList_Append(py_list, item); + Py_DECREF(item); + } + + return py_list; + } + +#if ENGINE_MINOR_VERSION >= 15 + if (auto casted_prop = Cast(prop)) + { + FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); + + PyObject* py_dict = PyDict_New(); + + for (int32 i = 0; i < map_helper.Num(); i++) + { + if (map_helper.IsValidIndex(i)) + { + + uint8* ptr = map_helper.GetPairPtr(i); + + PyObject* py_key = ue_py_convert_property(map_helper.KeyProp, ptr, 0); + if (!py_key) + { + Py_DECREF(py_dict); + return NULL; + } + + PyObject* py_value = ue_py_convert_property(map_helper.ValueProp, ptr, 0); + if (!py_value) + { + Py_DECREF(py_dict); + return NULL; + } + + PyDict_SetItem(py_dict, py_key, py_value); + Py_DECREF(py_key); + Py_DECREF(py_value); + } + } + + return py_dict; + } +#endif + + return PyErr_Format(PyExc_Exception, "unsupported value type %s for property %s", TCHAR_TO_UTF8(*prop->GetClass()->GetName()), TCHAR_TO_UTF8(*prop->GetName())); +} + +// convert a python object to a property +bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, int32 index) +{ + + if (PyBool_Check(py_obj)) + { + auto casted_prop = Cast(prop); + if (!casted_prop) + return false; + if (PyObject_IsTrue(py_obj)) + { + casted_prop->SetPropertyValue_InContainer(buffer, true, index); + } + else + { + casted_prop->SetPropertyValue_InContainer(buffer, false, index); + } + return true; + } + + if (PyNumber_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + PyObject* py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLong(py_long), index); + Py_DECREF(py_long); + return true; + } + if (auto casted_prop = Cast(prop)) + { + PyObject* py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); + Py_DECREF(py_long); + return true; + } + if (auto casted_prop = Cast(prop)) + { + PyObject* py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLongLong(py_long), index); + Py_DECREF(py_long); + return true; + } + if (auto casted_prop = Cast(prop)) + { + PyObject* py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLongLong(py_long), index); + Py_DECREF(py_long); + return true; + } + if (auto casted_prop = Cast(prop)) + { + PyObject* py_float = PyNumber_Float(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyFloat_AsDouble(py_float), index); + Py_DECREF(py_float); + return true; + } + if (auto casted_prop = Cast(prop)) + { + PyObject* py_long = PyNumber_Long(py_obj); + casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); + Py_DECREF(py_long); + return true; + } +#if ENGINE_MINOR_VERSION >= 15 + if (auto casted_prop = Cast(prop)) + { + PyObject* py_long = PyNumber_Long(py_obj); + void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); + casted_prop->GetUnderlyingProperty()->SetIntPropertyValue(prop_addr, (uint64)PyLong_AsUnsignedLong(py_long)); + Py_DECREF(py_long); + return true; + } +#endif + + + return false; + } + + if (PyUnicodeOrString_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + casted_prop->SetPropertyValue_InContainer(buffer, UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj)), index); + return true; + } + if (auto casted_prop = Cast(prop)) + { + casted_prop->SetPropertyValue_InContainer(buffer, UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj)), index); + return true; + } + if (auto casted_prop = Cast(prop)) + { + casted_prop->SetPropertyValue_InContainer(buffer, FText::FromString(UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj))), index); + return true; + } + return false; + } + + if (PyBytes_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); + + if (auto item_casted_prop = Cast(casted_prop->Inner)) + { + + Py_ssize_t pybytes_len = PyBytes_Size(py_obj); + uint8* buf = (uint8*)PyBytes_AsString(py_obj); + + + // fix array helper size + if (helper.Num() < pybytes_len) + { + helper.AddValues(pybytes_len - helper.Num()); + } + else if (helper.Num() > pybytes_len) + { + helper.RemoveValues(pybytes_len, helper.Num() - pybytes_len); + } + + + FMemory::Memcpy(helper.GetRawPtr(), buf, pybytes_len); + return true; + } + } + + return false; + } + + if (PyByteArray_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); + + if (auto item_casted_prop = Cast(casted_prop->Inner)) + { + + Py_ssize_t pybytes_len = PyByteArray_Size(py_obj); + uint8* buf = (uint8*)PyByteArray_AsString(py_obj); + + + // fix array helper size + if (helper.Num() < pybytes_len) + { + helper.AddValues(pybytes_len - helper.Num()); + } + else if (helper.Num() > pybytes_len) + { + helper.RemoveValues(pybytes_len, helper.Num() - pybytes_len); + } + + + FMemory::Memcpy(helper.GetRawPtr(), buf, pybytes_len); + + return true; + } + } + + return false; + } + + if (PyList_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); + + UProperty* array_prop = casted_prop->Inner; + Py_ssize_t pylist_len = PyList_Size(py_obj); + + // fix array helper size + if (helper.Num() < pylist_len) + { + helper.AddValues(pylist_len - helper.Num()); + } + else if (helper.Num() > pylist_len) + { + helper.RemoveValues(pylist_len, helper.Num() - pylist_len); + } + + for (int i = 0; i < (int)pylist_len; i++) + { + PyObject* py_item = PyList_GetItem(py_obj, i); + if (!ue_py_convert_pyobject(py_item, array_prop, helper.GetRawPtr(i), 0)) + { + return false; + } + } + return true; + } + + return false; + } + + if (PyTuple_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); + + UProperty* array_prop = casted_prop->Inner; + Py_ssize_t pytuple_len = PyTuple_Size(py_obj); + + // fix array helper size + if (helper.Num() < pytuple_len) + { + helper.AddValues(pytuple_len - helper.Num()); + } + else if (helper.Num() > pytuple_len) + { + helper.RemoveValues(pytuple_len, helper.Num() - pytuple_len); + } + + for (int i = 0; i < (int)pytuple_len; i++) + { + PyObject* py_item = PyTuple_GetItem(py_obj, i); + if (!ue_py_convert_pyobject(py_item, array_prop, helper.GetRawPtr(i), 0)) + { + return false; + } + } + return true; + } + + return false; + } + +#if ENGINE_MINOR_VERSION >= 15 + if (PyDict_Check(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); + + PyObject* py_key = nullptr; + PyObject* py_value = nullptr; + Py_ssize_t pos = 0; + + map_helper.EmptyValues(); + while (PyDict_Next(py_obj, &pos, &py_key, &py_value)) + { + + int32 hindex = map_helper.AddDefaultValue_Invalid_NeedsRehash(); + uint8* ptr = map_helper.GetPairPtr(hindex); + + if (!ue_py_convert_pyobject(py_key, casted_prop->KeyProp, ptr, 0)) + { + return false; + } + + if (!ue_py_convert_pyobject(py_value, casted_prop->ValueProp, ptr, 0)) + { + return false; + } + } + map_helper.Rehash(); + + return true; + } + + return false; + } +#endif + + // structs + + if (ue_PyFVector * py_vec = py_ue_is_fvector(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == TBaseStructure::Get()) + { + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_vec->vec; + return true; + } + } + return false; + } + + if (ue_PyFVector2D * py_vec = py_ue_is_fvector2d(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == TBaseStructure::Get()) + { + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_vec->vec; + return true; + } + } + return false; + } + + if (ue_PyFRotator * py_rot = py_ue_is_frotator(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == TBaseStructure::Get()) + { + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_rot->rot; + return true; + } + } + return false; + } + + if (ue_PyFTransform * py_transform = py_ue_is_ftransform(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == TBaseStructure::Get()) + { + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_transform->transform; + return true; + } + } + return false; + } + + if (ue_PyFColor * py_color = py_ue_is_fcolor(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == TBaseStructure::Get()) + { + + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_color->color; + return true; + } + } + return false; + } + + if (ue_PyFLinearColor * py_color = py_ue_is_flinearcolor(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == TBaseStructure::Get()) + { + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_color->color; + return true; + } + } + return false; + } + + if (ue_PyFHitResult * py_hit = py_ue_is_fhitresult(py_obj)) + { + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == FHitResult::StaticStruct()) + { + *casted_prop->ContainerPtrToValuePtr(buffer, index) = py_hit->hit; + return true; + } + } + return false; + } + + // generic structs + if (py_ue_is_uscriptstruct(py_obj)) + { + ue_PyUScriptStruct* py_u_struct = (ue_PyUScriptStruct*)py_obj; + if (auto casted_prop = Cast(prop)) + { + if (casted_prop->Struct == py_u_struct->u_struct) + { + uint8* dest = casted_prop->ContainerPtrToValuePtr(buffer, index); + py_u_struct->u_struct->InitializeStruct(dest); + py_u_struct->u_struct->CopyScriptStruct(dest, py_u_struct->u_struct_ptr); + return true; + } + } + return false; + } + + if (PyObject_IsInstance(py_obj, (PyObject*)& ue_PyUObjectType)) + { + ue_PyUObject* ue_obj = (ue_PyUObject*)py_obj; + if (ue_obj->ue_object->IsA()) + { + if (auto casted_prop = Cast(prop)) + { + casted_prop->SetPropertyValue_InContainer(buffer, ue_obj->ue_object, index); + return true; + } + else if (auto casted_prop_soft_class = Cast(prop)) + { + casted_prop_soft_class->SetPropertyValue_InContainer(buffer, FSoftObjectPtr(ue_obj->ue_object), index); + return true; + } + else if (auto casted_prop_soft_object = Cast(prop)) + { + + casted_prop_soft_object->SetPropertyValue_InContainer(buffer, FSoftObjectPtr(ue_obj->ue_object), index); + + return true; + } + else if (auto casted_prop_weak_object = Cast(prop)) + { + + casted_prop_weak_object->SetPropertyValue_InContainer(buffer, FWeakObjectPtr(ue_obj->ue_object), index); + + return true; + } + else if (auto casted_prop_base = Cast(prop)) + { + // ensure the object type is correct, otherwise crash could happen (soon or later) + if (!ue_obj->ue_object->IsA(casted_prop_base->PropertyClass)) + return false; + + casted_prop_base->SetObjectPropertyValue_InContainer(buffer, ue_obj->ue_object, index); + + return true; + } + + return false; + } + + + if (ue_obj->ue_object->IsA()) + { + if (auto casted_prop = Cast(prop)) + { + // if the property specifies an interface, the object must be of a class that implements it + if (casted_prop->PropertyClass->HasAnyClassFlags(CLASS_Interface)) + { + if (!ue_obj->ue_object->GetClass()->ImplementsInterface(casted_prop->PropertyClass)) + return false; + } + else + { + // ensure the object type is correct, otherwise crash could happen (soon or later) + if (!ue_obj->ue_object->IsA(casted_prop->PropertyClass)) + return false; + } + + casted_prop->SetObjectPropertyValue_InContainer(buffer, ue_obj->ue_object, index); + + return true; + } + else if (auto casted_prop_soft_object = Cast(prop)) + { + if (!ue_obj->ue_object->IsA(casted_prop_soft_object->PropertyClass)) + return false; + + casted_prop_soft_object->SetPropertyValue_InContainer(buffer, FSoftObjectPtr(ue_obj->ue_object), index); + + return true; + } + else if (auto casted_prop_interface = Cast(prop)) + { + // ensure the object type is correct, otherwise crash could happen (soon or later) + if (!ue_obj->ue_object->GetClass()->ImplementsInterface(casted_prop_interface->InterfaceClass)) + return false; + + casted_prop_interface->SetPropertyValue_InContainer(buffer, FScriptInterface(ue_obj->ue_object), index); + + return true; + } + } + return false; + } + + if (py_obj == Py_None) + { + auto casted_prop_class = Cast(prop); + if (casted_prop_class) + { + + casted_prop_class->SetPropertyValue_InContainer(buffer, nullptr, index); + + return true; + } + auto casted_prop = Cast(prop); + if (casted_prop) + { + + casted_prop->SetObjectPropertyValue_InContainer(buffer, nullptr, index); + + return true; + } + return false; + } + + return false; + +} +#endif + + +// check if a python object is a wrapper to a UObject +ue_PyUObject* ue_is_pyuobject(PyObject* obj) +{ + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_is_pyuobject: in obj %p type %p"), obj, Py_TYPE((PyObject*)& ue_PyUObjectType)); + if (!PyObject_IsInstance(obj, (PyObject*)& ue_PyUObjectType)) + return nullptr; + return (ue_PyUObject*)obj; +} + +#if ENGINE_MINOR_VERSION >= 25 +// check if a python object is a wrapper to an FProperty +ue_PyFProperty* ue_is_pyfproperty(PyObject* obj) +{ + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_is_pyfproperty: in obj %p type %p"), obj, Py_TYPE((PyObject*)& ue_PyFPropertyType)); + if (!PyObject_IsInstance(obj, (PyObject*)& ue_PyFPropertyType)) + return nullptr; + return (ue_PyFProperty*)obj; +} + +// check if a python object is a wrapper to an FFieldClass +ue_PyFFieldClass* ue_is_pyffieldclass(PyObject* obj) +{ + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_is_pyffieldclass: in obj %p type %p"), obj, Py_TYPE((PyObject*)& ue_PyFFieldClassType)); + if (!PyObject_IsInstance(obj, (PyObject*)& ue_PyFFieldClassType)) + return nullptr; + return (ue_PyFFieldClass*)obj; +} +#endif + +void ue_bind_events_for_py_class_by_attribute(UObject* u_obj, PyObject* py_class) +{ + // attempt to register events + PyObject* attrs = PyObject_Dir(py_class); + if (!attrs) + return; + + AActor* actor = Cast(u_obj); + if (!actor) + { + UActorComponent* component = Cast(u_obj); + if (!component) + return; + actor = component->GetOwner(); + } + + Py_ssize_t len = PyList_Size(attrs); + for (Py_ssize_t i = 0; i < len; i++) + { + PyObject* py_attr_name = PyList_GetItem(attrs, i); + if (!py_attr_name || !PyUnicodeOrString_Check(py_attr_name)) + continue; + PyObject* item = PyObject_GetAttrString(py_class, UEPyUnicode_AsUTF8(py_attr_name)); + if (item && PyCallable_Check(item)) + { + // check for ue_event signature + PyObject* event_signature = PyObject_GetAttrString(item, (char*)"ue_event"); + if (event_signature) + { + if (PyUnicodeOrString_Check(event_signature)) + { + FString event_name = FString(UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(event_signature))); + TArray parts; + int n = event_name.ParseIntoArray(parts, UTF8_TO_TCHAR(".")); + if (n < 1 || n > 2) + { + PyErr_SetString(PyExc_Exception, "invalid ue_event syntax, must be the name of an event or ComponentName.Event"); + unreal_engine_py_log_error(); + } + else + { + if (n == 1) + { + if (!ue_bind_pyevent(ue_get_python_uobject(actor), parts[0], item, true)) + { + unreal_engine_py_log_error(); + } + } + else + { + bool found = false; + for (UActorComponent* component : actor->GetComponents()) + { + if (component->GetFName() == FName(*parts[0])) + { + if (!ue_bind_pyevent(ue_get_python_uobject(component), parts[1], item, true)) + { + unreal_engine_py_log_error(); + } + found = true; + break; + } + } + + if (!found) + { + PyErr_SetString(PyExc_Exception, "unable to find component by name"); + unreal_engine_py_log_error(); } } } @@ -2903,12 +4121,21 @@ void ue_autobind_events_for_pyclass(ue_PyUObject* u_obj, PyObject* py_class) static void py_ue_destroy_params(UFunction* u_function, uint8* buffer) { // destroy params +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator DArgs(u_function); + for (; DArgs && (DArgs->PropertyFlags & CPF_Parm); ++DArgs) + { + FProperty* prop = *DArgs; + prop->DestroyValue_InContainer(buffer); + } +#else TFieldIterator DArgs(u_function); for (; DArgs && (DArgs->PropertyFlags & CPF_Parm); ++DArgs) { UProperty* prop = *DArgs; prop->DestroyValue_InContainer(buffer); } +#endif } PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* args, int argn, PyObject* kwargs) @@ -2932,9 +4159,17 @@ PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* uint8* buffer = (uint8*)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); // initialize args +#if ENGINE_MINOR_VERSION >= 25 + for (TFieldIterator IArgs(u_function); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) +#else for (TFieldIterator IArgs(u_function); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) +#endif { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* prop = *IArgs; +#else UProperty* prop = *IArgs; +#endif if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) { prop->InitializeValue_InContainer(buffer); @@ -2967,10 +4202,18 @@ PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* int has_out_params = 0; +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator PArgs(u_function); +#else TFieldIterator PArgs(u_function); +#endif for (; PArgs && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* prop = *PArgs; +#else UProperty* prop = *PArgs; +#endif if (argn < tuple_len) { PyObject* py_arg = PyTuple_GetItem(args, argn); @@ -2998,7 +4241,11 @@ PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* } } } +#if ENGINE_MINOR_VERSION >= 25 + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) +#else if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) +#endif { has_out_params++; } @@ -3015,10 +4262,18 @@ PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* PyObject* ret = nullptr; int has_ret_param = 0; +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator Props(u_function); +#else TFieldIterator Props(u_function); +#endif for (; Props; ++Props) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* prop = *Props; +#else UProperty* prop = *Props; +#endif if (prop->GetPropertyFlags() & CPF_ReturnParm) { ret = ue_py_convert_property(prop, buffer, 0); @@ -3040,11 +4295,20 @@ PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* { PyTuple_SetItem(multi_ret, 0, ret); } +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator OProps(u_function); +#else TFieldIterator OProps(u_function); +#endif for (; OProps; ++OProps) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* prop = *OProps; + if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) +#else UProperty* prop = *OProps; if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) +#endif { // skip return param as it must be always the first if (prop->GetPropertyFlags() & CPF_ReturnParm) @@ -3077,17 +4341,31 @@ PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* PyObject* ue_unbind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* py_callable, bool fail_on_wrong_property) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* f_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); + if (!f_property) +#else UProperty* u_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); if (!u_property) +#endif { if (fail_on_wrong_property) return PyErr_Format(PyExc_Exception, "unable to find event property %s", TCHAR_TO_UTF8(*event_name)); Py_RETURN_NONE; } +#if ENGINE_MINOR_VERSION >= 25 + if (auto casted_prop = CastField(f_property)) +#else if (auto casted_prop = Cast(u_property)) +#endif { +#if ENGINE_MINOR_VERSION >= 25 + // can we reuse UPythonDelegate here?? + UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->FindDelegate(u_obj->ue_object, py_callable); +#else UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->FindDelegate(u_obj->ue_object, py_callable); +#endif if (py_delegate != nullptr) { #if ENGINE_MINOR_VERSION < 23 @@ -3106,6 +4384,16 @@ PyObject* ue_unbind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* p #endif } } +#if ENGINE_MINOR_VERSION >= 25 + else if (auto casted_prop_delegate = CastField(f_property)) + { + FScriptDelegate script_delegate = casted_prop_delegate->GetPropertyValue_InContainer(u_obj->ue_object); + script_delegate.Unbind(); + + // re-assign multicast delegate + casted_prop_delegate->SetPropertyValue_InContainer(u_obj->ue_object, script_delegate); + } +#else else if (auto casted_prop_delegate = Cast(u_property)) { FScriptDelegate script_delegate = casted_prop_delegate->GetPropertyValue_InContainer(u_obj->ue_object); @@ -3114,6 +4402,7 @@ PyObject* ue_unbind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* p // re-assign multicast delegate casted_prop_delegate->SetPropertyValue_InContainer(u_obj->ue_object, script_delegate); } +#endif else { if (fail_on_wrong_property) @@ -3126,15 +4415,24 @@ PyObject* ue_unbind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* p PyObject* ue_bind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* py_callable, bool fail_on_wrong_property) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* f_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); + if (!f_property) +#else UProperty* u_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); if (!u_property) +#endif { if (fail_on_wrong_property) return PyErr_Format(PyExc_Exception, "unable to find event property %s", TCHAR_TO_UTF8(*event_name)); Py_RETURN_NONE; } +#if ENGINE_MINOR_VERSION >= 25 + if (auto casted_prop = CastField(f_property)) +#else if (auto casted_prop = Cast(u_property)) +#endif { #if ENGINE_MINOR_VERSION < 23 FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); @@ -3143,7 +4441,12 @@ PyObject* ue_bind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* py_ #endif FScriptDelegate script_delegate; +#if ENGINE_MINOR_VERSION >= 25 + // can we reuse UPythonDelegate here?? + UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); +#else UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); +#endif // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -3157,11 +4460,20 @@ PyObject* ue_bind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* py_ casted_prop->SetMulticastDelegate(u_obj->ue_object, multiscript_delegate); #endif } +#if ENGINE_MINOR_VERSION >= 25 + else if (auto casted_prop_delegate = CastField(f_property)) +#else else if (auto casted_prop_delegate = Cast(u_property)) +#endif { FScriptDelegate script_delegate = casted_prop_delegate->GetPropertyValue_InContainer(u_obj->ue_object); +#if ENGINE_MINOR_VERSION >= 25 + // can we reuse UPythonDelegate here?? + UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop_delegate->SignatureFunction); +#else UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop_delegate->SignatureFunction); +#endif // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -3229,8 +4541,13 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ PyObject* annotations = PyObject_GetAttrString(py_callable, "__annotations__"); +#if ENGINE_MINOR_VERSION >= 25 + FField** next_property = &function->ChildProperties; + FProperty** next_property_link = &function->PropertyLink; +#else UField** next_property = &function->Children; UProperty** next_property_link = &function->PropertyLink; +#endif PyObject* parameters_keys = PyObject_GetIter(parameters); // do not process args if no annotations are available @@ -3253,6 +4570,135 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ if (!value) continue; +#if ENGINE_MINOR_VERSION >= 25 + FProperty* prop = nullptr; + if (PyType_Check(value)) + { + if ((PyTypeObject*)value == &PyFloat_Type) + { + prop = new FFloatProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)value == &PyUnicode_Type) + { + prop = new FStrProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)value == &PyBool_Type) + { + prop = new FBoolProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)value == &PyLong_Type) + { + prop = new FIntProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)value == &ue_PyFVectorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFVector2DType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFRotatorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFLinearColorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFColorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFTransformType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFQuatType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if (PyObject_IsInstance(value, (PyObject*)& PyType_Type)) + { + // Method annotation like foo:typing.Type[Pawn] produces annotations like typing.Type[Pawn], with .__args__ = (Pawn,) + PyObject* type_args = PyObject_GetAttrString(value, "__args__"); + if (!type_args) + { + UE_LOG(LogPython, Error, TEXT("missing type info on %s"), UTF8_TO_TCHAR(name)); + return nullptr; + } + if (PyTuple_Size(type_args) != 1) + { + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("exactly one class is allowed in type info for %s"), UTF8_TO_TCHAR(name)); + return nullptr; + } + PyObject* py_class = PyTuple_GetItem(type_args, 0); + ue_PyUObject* py_obj = ue_is_pyuobject(py_class); + if (!py_obj) + { + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("type for %s must be a ue_PyUObject"), UTF8_TO_TCHAR(name)); + return nullptr; + } + if (!py_obj->ue_object->IsA()) + { + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("type for %s must be a UClass"), UTF8_TO_TCHAR(name)); + return nullptr; + } + FClassProperty* prop_class = new FClassProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_class->SetMetaClass((UClass*)py_obj->ue_object); + prop_class->PropertyClass = UClass::StaticClass(); + prop = prop_class; + Py_DECREF(type_args); + } + } + else if (ue_PyUObject * py_obj = ue_is_pyuobject(value)) + { + if (py_obj->ue_object->IsA()) + { + UClass* p_u_class = (UClass*)py_obj->ue_object; + FObjectProperty* prop_base = new FObjectProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_base->SetPropertyClass(p_u_class); + prop = prop_base; + } + else if (py_obj->ue_object->IsA()) + { + FEnumProperty* prop_enum = new FEnumProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + FNumericProperty* prop_underlying = new FByteProperty(prop_enum, TEXT("UnderlyingType"), RF_Public); + prop_enum->SetEnum((UEnum*)py_obj->ue_object); + prop_enum->AddCppProperty(prop_underlying); + prop = prop_enum; + } + else if (py_obj->ue_object->IsA()) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = (UScriptStruct*)py_obj->ue_object; + prop = prop_struct; + } + } + // not clear if we are going to need this now + // currently dont think so as dont see we will have an FProperty as a python type annotation + //else if (ue_PyFProperty * py_obj = ue_is_pyfproperty(value)) + //{ + // UE_LOG(LogPython, Error, TEXT("FProperty in "), UTF8_TO_TCHAR(name)); + //} +#else UProperty* prop = nullptr; if (PyType_Check(value)) { @@ -3378,6 +4824,7 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ prop = prop_struct; } } +#endif if (prop) { @@ -3401,6 +4848,130 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ if (py_return_value) { UE_LOG(LogPython, Warning, TEXT("Return Value found")); +#if ENGINE_MINOR_VERSION >= 25 + FProperty* prop = nullptr; + char* p_name = (char*) "ReturnValue"; + if (PyType_Check(py_return_value)) + { + if ((PyTypeObject*)py_return_value == &PyFloat_Type) + { + prop = new FFloatProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)py_return_value == &PyUnicode_Type) + { + prop = new FStrProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)py_return_value == &PyBool_Type) + { + prop = new FBoolProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)py_return_value == &PyLong_Type) + { + prop = new FIntProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + } + else if ((PyTypeObject*)py_return_value == &ue_PyFVectorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFVector2DType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFRotatorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFLinearColorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFColorType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFTransformType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFQuatType) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if (PyObject_IsInstance(py_return_value, (PyObject*)& PyType_Type)) + { + // Method annotation like foo:typing.Type[Pawn] produces annotations like typing.Type[Pawn], with .__args__ = (Pawn,) + PyObject* type_args = PyObject_GetAttrString(py_return_value, "__args__"); + if (!type_args) + { + UE_LOG(LogPython, Error, TEXT("missing type info on %s"), UTF8_TO_TCHAR(name)); + return nullptr; + } + if (PyTuple_Size(type_args) != 1) + { + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("exactly one class is allowed in type info for %s"), UTF8_TO_TCHAR(name)); + return nullptr; + } + PyObject* py_class = PyTuple_GetItem(type_args, 0); + ue_PyUObject* py_obj = ue_is_pyuobject(py_class); + if (!py_obj) + { + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("type for %s must be a ue_PyUObject"), UTF8_TO_TCHAR(name)); + return nullptr; + } + if (!py_obj->ue_object->IsA()) + { + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("type for %s must be a UClass"), UTF8_TO_TCHAR(name)); + return nullptr; + } + FClassProperty* prop_class = new FClassProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_class->SetMetaClass((UClass*)py_obj->ue_object); + prop_class->PropertyClass = UClass::StaticClass(); + prop = prop_class; + Py_DECREF(type_args); + } + } + else if (ue_PyUObject * py_obj = ue_is_pyuobject(py_return_value)) + { + if (py_obj->ue_object->IsA()) + { + UClass* p_u_class = (UClass*)py_obj->ue_object; + FObjectProperty* prop_base = new FObjectProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_base->SetPropertyClass(p_u_class); + prop = prop_base; + } + else if (py_obj->ue_object->IsA()) + { + FEnumProperty* prop_enum = new FEnumProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + FNumericProperty* prop_underlying = new FByteProperty(prop_enum, TEXT("UnderlyingType"), RF_Public); + prop_enum->SetEnum((UEnum*)py_obj->ue_object); + prop_enum->AddCppProperty(prop_underlying); + prop = prop_enum; + } + else if (py_obj->ue_object->IsA()) + { + FStructProperty* prop_struct = new FStructProperty(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = (UScriptStruct*)py_obj->ue_object; + prop = prop_struct; + } + } +#else UProperty* prop = nullptr; char* p_name = (char*) "ReturnValue"; if (PyType_Check(py_return_value)) @@ -3527,6 +5098,7 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ prop = prop_struct; } } +#endif if (prop) { @@ -3552,33 +5124,65 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ if (!function->IsSignatureCompatibleWith(parent_function)) { +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator It(parent_function); +#else TFieldIterator It(parent_function); +#endif while (It) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* p = *It; +#else UProperty* p = *It; +#endif if (p->PropertyFlags & CPF_Parm) { UE_LOG(LogPython, Warning, TEXT("Parent PROP: %s %d/%d %d %d %d %s %p"), *p->GetName(), (int)p->PropertyFlags, (int)UFunction::GetDefaultIgnoredSignatureCompatibilityFlags(), (int)(p->PropertyFlags & ~UFunction::GetDefaultIgnoredSignatureCompatibilityFlags()), p->GetSize(), p->GetOffset_ForGC(), *p->GetClass()->GetName(), p->GetClass()); +#if ENGINE_MINOR_VERSION >= 25 + FClassProperty* ucp = CastField(p); +#else UClassProperty* ucp = Cast(p); +#endif if (ucp) { +#if ENGINE_MINOR_VERSION >= 25 + UE_LOG(LogPython, Warning, TEXT("Parent FClassProperty = %p %s %p %s"), ucp->PropertyClass, *ucp->PropertyClass->GetName(), ucp->MetaClass, *ucp->MetaClass->GetName()); +#else UE_LOG(LogPython, Warning, TEXT("Parent UClassProperty = %p %s %p %s"), ucp->PropertyClass, *ucp->PropertyClass->GetName(), ucp->MetaClass, *ucp->MetaClass->GetName()); +#endif } } ++It; } +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator It2(function); +#else TFieldIterator It2(function); +#endif while (It2) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* p = *It2; +#else UProperty* p = *It2; +#endif if (p->PropertyFlags & CPF_Parm) { UE_LOG(LogPython, Warning, TEXT("Function PROP: %s %d/%d %d %d %d %s %p"), *p->GetName(), (int)p->PropertyFlags, (int)UFunction::GetDefaultIgnoredSignatureCompatibilityFlags(), (int)(p->PropertyFlags & ~UFunction::GetDefaultIgnoredSignatureCompatibilityFlags()), p->GetSize(), p->GetOffset_ForGC(), *p->GetClass()->GetName(), p->GetClass()); +#if ENGINE_MINOR_VERSION >= 25 + FClassProperty* ucp = CastField(p); +#else UClassProperty* ucp = Cast(p); +#endif if (ucp) { +#if ENGINE_MINOR_VERSION >= 25 + UE_LOG(LogPython, Warning, TEXT("Function FClassProperty = %p %s %p %s"), ucp->PropertyClass, *ucp->PropertyClass->GetName(), ucp->MetaClass, *ucp->MetaClass->GetName()); +#else UE_LOG(LogPython, Warning, TEXT("Function UClassProperty = %p %s %p %s"), ucp->PropertyClass, *ucp->PropertyClass->GetName(), ucp->MetaClass, *ucp->MetaClass->GetName()); +#endif } } ++It2; @@ -3592,10 +5196,18 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ function->NumParms = 0; // allocate properties storage (ignore super) +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator props(function, EFieldIteratorFlags::ExcludeSuper); +#else TFieldIterator props(function, EFieldIteratorFlags::ExcludeSuper); +#endif for (; props; ++props) { +#if ENGINE_MINOR_VERSION >= 25 + FProperty* p = *props; +#else UProperty* p = *props; +#endif if (p->HasAnyPropertyFlags(CPF_Parm)) { function->NumParms++; diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 9d865cb16..8d9b997ab 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -24,12 +24,20 @@ #endif #endif - UWorld *ue_get_uworld(ue_PyUObject *); AActor *ue_get_actor(ue_PyUObject *); +#if ENGINE_MINOR_VERSION >= 25 +PyObject *ue_py_convert_property(FProperty *, uint8 *, int32); +bool ue_py_convert_pyobject(PyObject *, FProperty *, uint8 *, int32); +#else PyObject *ue_py_convert_property(UProperty *, uint8 *, int32); bool ue_py_convert_pyobject(PyObject *, UProperty *, uint8 *, int32); +#endif ue_PyUObject *ue_is_pyuobject(PyObject *); +#if ENGINE_MINOR_VERSION >= 25 +ue_PyFProperty *ue_is_pyfproperty(PyObject *); +ue_PyFFieldClass *ue_is_pyffieldclass(PyObject *); +#endif void ue_bind_events_for_py_class_by_attribute(UObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPySubclassing.cpp b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp index 548a83935..018064e94 100644 --- a/Source/UnrealEnginePython/Private/UEPySubclassing.cpp +++ b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp @@ -1,7 +1,10 @@ + #include "UEPyModule.h" #include "PythonClass.h" #include "UObject/UEPyObject.h" +//#include "Runtime/CoreUObject/Public/UObject/Field.h" + // hack for avoiding loops in class constructors (thanks to the Unreal.js project for the idea) UClass *ue_py_class_constructor_placeholder = nullptr; static void UEPyClassConstructor(UClass *u_class, const FObjectInitializer &ObjectInitializer) @@ -16,6 +19,7 @@ static void UEPyClassConstructor(UClass *u_class, const FObjectInitializer &Obje int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) { + EXTRA_UE_LOG(LogPython, Warning, TEXT("unreal_engine_py_init (uobject) - subclassing args %d"), PyTuple_Size(args)); // is it subclassing ? if (PyTuple_Size(args) == 3) @@ -68,14 +72,104 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) continue; } + //EXTRA_UE_LOG(LogPython, Warning, TEXT("object init attr value %p"), value); + bool prop_added = false; + + //EXTRA_UE_LOG(LogPython, Warning, TEXT("offset tp_alloc %d %x"), offsetof(PyTypeObject, tp_alloc), offsetof(PyTypeObject, tp_alloc)); + +#if ENGINE_MINOR_VERSION >= 25 + if (FProperty *f_property = new_class->FindPropertyByName(FName(UTF8_TO_TCHAR(class_key)))) + { + UE_LOG(LogPython, Warning, TEXT("Found FProperty %s"), UTF8_TO_TCHAR(class_key)); + PyDict_SetItem(py_additional_properties, key, value); + prop_added = true; + } +#else if (UProperty *u_property = new_class->FindPropertyByName(FName(UTF8_TO_TCHAR(class_key)))) { UE_LOG(LogPython, Warning, TEXT("Found UProperty %s"), UTF8_TO_TCHAR(class_key)); PyDict_SetItem(py_additional_properties, key, value); prop_added = true; } +#endif +#if ENGINE_MINOR_VERSION >= 25 + // add simple property + // this is confusing because it appears when properties were uobjects UEP allowed both general + // uobject classes and UProperty classes as arguments - creating a UOBjectProperty/UScriptStructProperty for a non-UProperty class + // this now has to be separate code + else if (ue_is_pyfproperty(value)) + { + // So I dont think an FProperty is valid as an argument here - we need a class object + // which for FProperties is now an FFieldClass + // we could of course take the class of the FProperty and add a new property + //ue_PyFProperty *py_obj = (ue_PyFProperty *)value; + UE_LOG(LogPython, Error, TEXT("fproperty not valid as property argument %s"), UTF8_TO_TCHAR(class_key)); + return -1; + } + else if (ue_is_pyffieldclass(value)) + { + //ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)value; + //UE_LOG(LogPython, Error, TEXT("simple property NEEDS FIXING %s"), UTF8_TO_TCHAR(class_key)); + //return -1; + // so I think we just add the property here now - in old terms we essentially know + // this is a UClass of a UProperty subclass - ie an FFieldClass + // think we should disallow a plain FFieldClass - we need a subclass + ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)value; + //if (FFieldClass::IsChildOf(py_obj->ue_ffieldclass)) + //{ + if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + //} + } + // allow for adding UObject/UScriptStruct properties by simply specifying the UObject/UScriptStruct class here + // this appears to be what the UProperty code did + else if (ue_is_pyuobject(value)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)value; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + // So apparently this wont work - UProperty subclasses seems to be mapped to FFieldClass automagically + //if (p_class->IsChildOf()) + //{ + // we may be able to leave this in as it appears we still have access to UProperty subclasses + // for the moment error out here + //if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + //{ + // unreal_engine_py_log_error(); + // return -1; + //} + //prop_added = true; + //UE_LOG(LogPython, Error, TEXT("uproperty as property implementation disabled ")); + //return -1; + //} + //else + { + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_ffieldclass(FObjectProperty::StaticClass()), class_key, value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + else if (py_obj->ue_object->IsA()) + { + if (!py_ue_add_property(self, Py_BuildValue("(OsO)", (PyObject *)ue_get_python_ffieldclass(FStructProperty::StaticClass()), class_key, value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } +#else // add simple property else if (ue_is_pyuobject(value)) { @@ -112,6 +206,7 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) prop_added = true; } } +#endif // add array property else if (PyList_Check(value)) @@ -119,6 +214,77 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) if (PyList_Size(value) == 1) { PyObject *first_item = PyList_GetItem(value, 0); +#if ENGINE_MINOR_VERSION >= 25 + // pigs - we need same code as above - allow for direct FFieldClass plus UObject/UScriptStruct classes + if (ue_is_pyfproperty(first_item)) + { + // So I dont think an FProperty is valid as an argument here - we need a class object + // which for FProperties is now an FFieldClass + // we could of course take the class of the FProperty and add a new property + //ue_PyFProperty *py_obj = (ue_PyFProperty *)first_item; + UE_LOG(LogPython, Error, TEXT("fproperty not valid as property argument %s"), UTF8_TO_TCHAR(class_key)); + return -1; + } + else if (ue_is_pyffieldclass(first_item)) + { + //ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)value; + //UE_LOG(LogPython, Error, TEXT("simple property NEEDS FIXING %s"), UTF8_TO_TCHAR(class_key)); + //return -1; + // so I think we just add the property here now - in old terms we essentially know + // this is a UClass of a UProperty subclass - ie an FFieldClass + // think we should disallow a plain FFieldClass - we need a subclass + ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)first_item; + //if (py_obj->ue_ffieldclass->IsChildOf(FFieldClass::StaticClass())) + //{ + if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + //} + } + else if (ue_is_pyuobject(first_item)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)first_item; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + // So apparently this wont work - UProperty subclasses seems to be mapped to FFieldClass automagically + //if (p_class->IsChildOf()) + //{ + // we may be able to leave this in as it appears we still have access to UProperty subclasses + // for the moment error out here + //if (!py_ue_add_property(self, Py_BuildValue("(Os)", value, class_key))) + //{ + // unreal_engine_py_log_error(); + // return -1; + //} + //prop_added = true; + //UE_LOG(LogPython, Error, TEXT("uproperty as property array implementation disabled ")); + //return -1; + //} + //else + { + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_ffieldclass(FObjectProperty::StaticClass()), class_key, first_item))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } + else if (py_obj->ue_object->IsA()) + { + if (!py_ue_add_property(self, Py_BuildValue("([O]sO)", (PyObject *)ue_get_python_ffieldclass(FStructProperty::StaticClass()), class_key, first_item))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } +#else if (ue_is_pyuobject(first_item)) { ue_PyUObject *py_obj = (ue_PyUObject *)first_item; @@ -155,6 +321,7 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) prop_added = true; } } +#endif } } @@ -167,6 +334,101 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) PyObject *py_value = nullptr; Py_ssize_t pos = 0; PyDict_Next(value, &pos, &py_key, &py_value); +#if ENGINE_MINOR_VERSION >= 25 + if (ue_is_pyfproperty(py_key) || ue_is_pyfproperty(py_value)) + { + // So I dont think an FProperty is valid as an argument here - we need a class object + // which for FProperties is now an FFieldClass + // we could of course take the class of the FProperty and add a new property + //ue_PyFProperty *py_obj = (ue_PyFProperty *)first_item; + UE_LOG(LogPython, Error, TEXT("fproperty not valid as property argument %s"), UTF8_TO_TCHAR(class_key)); + return -1; + } + else + { + PyObject *first_item = nullptr; + PyObject *second_item = nullptr; + + if (ue_is_pyffieldclass(py_key)) + { + ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)py_key; + //if (py_obj->ue_ffieldclass->IsChildOf(FFieldClass::StaticClass())) + //{ + first_item = py_key; + //} + } + else if (ue_is_pyuobject(py_key)) + { + ue_PyUObject *py_obj = (ue_PyUObject *)py_key; + if (py_obj->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj->ue_object; + // So apparently this wont work - UProperty subclasses seems to be mapped to FFieldClass automagically + //if (p_class->IsChildOf()) + //{ + // we may be able to leave this in as it appears we still have access to UProperty subclasses + // for the moment error out here + //first_item = py_key; + //UE_LOG(LogPython, Error, TEXT("uproperty as property map key implementation disabled ")); + //return -1; + //} + //else + { + first_item = (PyObject *)ue_get_python_ffieldclass(FObjectProperty::StaticClass()); + } + } + else if (py_obj->ue_object->IsA()) + { + first_item = (PyObject *)ue_get_python_ffieldclass(FStructProperty::StaticClass()); + } + } + + if (ue_is_pyffieldclass(py_value)) + { + ue_PyFFieldClass *py_obj2 = (ue_PyFFieldClass *)py_key; + //if (py_obj2->ue_ffieldclass->IsChildOf(FFieldClass::StaticClass())) + //{ + second_item = py_value; + //} + } + else if (ue_is_pyuobject(py_value)) + { + ue_PyUObject *py_obj2 = (ue_PyUObject *)py_value; + + if (py_obj2->ue_object->IsA()) + { + UClass *p_class = (UClass *)py_obj2->ue_object; + // So apparently this wont work - UProperty subclasses seems to be mapped to FFieldClass automagically + //if (p_class->IsChildOf()) + //{ + // we may be able to leave this in as it appears we still have access to UProperty subclasses + // for the moment error out here + //second_item = py_value; + //UE_LOG(LogPython, Error, TEXT("uproperty as property map value implementation disabled ")); + //return -1; + //} + //else + { + second_item = (PyObject *)ue_get_python_ffieldclass(FObjectProperty::StaticClass()); + } + } + else if (py_obj2->ue_object->IsA()) + { + second_item = (PyObject *)ue_get_python_ffieldclass(FStructProperty::StaticClass()); + } + } + + if (first_item && second_item) + { + if (!py_ue_add_property(self, Py_BuildValue("([OO]sOO)", first_item, second_item, class_key, py_key, py_value))) + { + unreal_engine_py_log_error(); + return -1; + } + prop_added = true; + } + } +#else if (ue_is_pyuobject(py_key) && ue_is_pyuobject(py_value)) { PyObject *first_item = nullptr; @@ -215,8 +477,10 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) } prop_added = true; } +#endif } } + #endif // function ? else if (PyCallable_Check(value) && class_key[0] >= 'A' && class_key[0] <= 'Z') @@ -341,11 +605,19 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) PyObject *mc_value = PyDict_GetItem(found_additional_props, mc_key); const char *mc_name = UEPyUnicode_AsUTF8(mc_key); +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = ObjectInitializer.GetObj()->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(mc_name))); + if (f_property) + { + if (auto casted_prop = CastField(f_property)) + { +#else UProperty *u_property = ObjectInitializer.GetObj()->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(mc_name))); if (u_property) { if (auto casted_prop = Cast(u_property)) { +#endif #if ENGINE_MINOR_VERSION >= 23 FMulticastScriptDelegate multiscript_delegate = *casted_prop->GetMulticastDelegate(ObjectInitializer.GetObj()); #else @@ -354,7 +626,12 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) #endif FScriptDelegate script_delegate; +#if ENGINE_MINOR_VERSION >= 25 + // can we reuse UPythonDelegate here?? + UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); +#else UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); +#endif // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -504,4 +781,22 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) } return 0; -} \ No newline at end of file +} + + +#if ENGINE_MINOR_VERSION >= 25 + + +int unreal_engine_py_fproperty_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) +{ + UE_LOG(LogPython, Warning, TEXT("__init__ called (tp_init) for FProperty")); + return -1; +} + +int unreal_engine_py_ffieldclass_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) +{ + UE_LOG(LogPython, Warning, TEXT("__init__ called (tp_init) for FFieldClass")); + return -1; +} + +#endif diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp index 28df03059..bddd36ebf 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.cpp @@ -12,11 +12,19 @@ static PyObject *py_ue_uscriptstruct_get_field(ue_PyUScriptStruct *self, PyObjec return nullptr; } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", name); + + return ue_py_convert_property(f_property, self->u_struct_ptr, index); +#else UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", name); return ue_py_convert_property(u_property, self->u_struct_ptr, index); +#endif } static PyObject *py_ue_uscriptstruct_get_field_array_dim(ue_PyUScriptStruct *self, PyObject * args) @@ -27,11 +35,19 @@ static PyObject *py_ue_uscriptstruct_get_field_array_dim(ue_PyUScriptStruct *sel return nullptr; } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", name); + + return PyLong_FromLongLong(f_property->ArrayDim); +#else UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", name); return PyLong_FromLongLong(u_property->ArrayDim); +#endif } static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObject * args) @@ -44,6 +60,17 @@ static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObjec return nullptr; } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", name); + + + if (!ue_py_convert_pyobject(value, f_property, self->u_struct_ptr, index)) + { + return PyErr_Format(PyExc_Exception, "unable to set property %s", name); + } +#else UProperty *u_property = self->u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", name); @@ -53,6 +80,7 @@ static PyObject *py_ue_uscriptstruct_set_field(ue_PyUScriptStruct *self, PyObjec { return PyErr_Format(PyExc_Exception, "unable to set property %s", name); } +#endif Py_RETURN_NONE; @@ -62,6 +90,15 @@ static PyObject *py_ue_uscriptstruct_fields(ue_PyUScriptStruct *self, PyObject * { PyObject *ret = PyList_New(0); +#if ENGINE_MINOR_VERSION >= 25 + for (TFieldIterator PropIt(self->u_struct); PropIt; ++PropIt) + { + FProperty* property = *PropIt; + PyObject *property_name = PyUnicode_FromString(TCHAR_TO_UTF8(*property->GetName())); + PyList_Append(ret, property_name); + Py_DECREF(property_name); + } +#else for (TFieldIterator PropIt(self->u_struct); PropIt; ++PropIt) { UProperty* property = *PropIt; @@ -69,6 +106,7 @@ static PyObject *py_ue_uscriptstruct_fields(ue_PyUScriptStruct *self, PyObject * PyList_Append(ret, property_name); Py_DECREF(property_name); } +#endif return ret; } @@ -92,7 +130,11 @@ PyObject *py_ue_uscriptstruct_as_dict(ue_PyUScriptStruct * self, PyObject * args static const FName DisplayNameKey(TEXT("DisplayName")); PyObject *py_struct_dict = PyDict_New(); +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator SArgs(self->u_struct); +#else TFieldIterator SArgs(self->u_struct); +#endif for (; SArgs; ++SArgs) { PyObject *struct_value = ue_py_convert_property(*SArgs, self->u_struct_ptr, 0); @@ -143,20 +185,36 @@ static PyObject *ue_PyUScriptStruct_str(ue_PyUScriptStruct *self) TCHAR_TO_UTF8(*self->u_struct->GetName()), self->u_struct->GetStructureSize(), self->u_struct_ptr); } +#if ENGINE_MINOR_VERSION >= 25 +static FProperty *get_field_from_name(UScriptStruct *u_struct, char *name) +#else static UProperty *get_field_from_name(UScriptStruct *u_struct, char *name) +#endif { FString attr = UTF8_TO_TCHAR(name); +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(*attr)); + if (f_property) + return f_property; +#else UProperty *u_property = u_struct->FindPropertyByName(FName(*attr)); if (u_property) return u_property; +#endif #if WITH_EDITOR static const FName DisplayNameKey(TEXT("DisplayName")); // if the property is not found, attempt to search for DisplayName +#if ENGINE_MINOR_VERSION >= 25 + for (TFieldIterator prop(u_struct); prop; ++prop) + { + FProperty *property = *prop; +#else for (TFieldIterator prop(u_struct); prop; ++prop) { UProperty *property = *prop; +#endif if (property->HasMetaData(DisplayNameKey)) { FString display_name = property->GetMetaData(DisplayNameKey); @@ -171,7 +229,11 @@ static UProperty *get_field_from_name(UScriptStruct *u_struct, char *name) return nullptr; } +#if ENGINE_MINOR_VERSION >= 25 +FProperty *ue_struct_get_field_from_name(UScriptStruct *u_struct, char *name) +#else UProperty *ue_struct_get_field_from_name(UScriptStruct *u_struct, char *name) +#endif { return get_field_from_name(u_struct, name); } @@ -185,6 +247,15 @@ static PyObject *ue_PyUScriptStruct_getattro(ue_PyUScriptStruct *self, PyObject { const char *attr = UEPyUnicode_AsUTF8(attr_name); // first check for property +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = get_field_from_name(self->u_struct, (char *)attr); + if (f_property) + { + // swallow previous exception + PyErr_Clear(); + return ue_py_convert_property(f_property, self->u_struct_ptr, 0); + } +#else UProperty *u_property = get_field_from_name(self->u_struct, (char *)attr); if (u_property) { @@ -192,6 +263,7 @@ static PyObject *ue_PyUScriptStruct_getattro(ue_PyUScriptStruct *self, PyObject PyErr_Clear(); return ue_py_convert_property(u_property, self->u_struct_ptr, 0); } +#endif } } return ret; @@ -199,11 +271,23 @@ static PyObject *ue_PyUScriptStruct_getattro(ue_PyUScriptStruct *self, PyObject static int ue_PyUScriptStruct_setattro(ue_PyUScriptStruct *self, PyObject *attr_name, PyObject *value) { - // first of all check for UProperty + // first of all check for Property (FProperty or UProperty) if (PyUnicodeOrString_Check(attr_name)) { const char *attr = UEPyUnicode_AsUTF8(attr_name); // first check for property +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = get_field_from_name(self->u_struct, (char *)attr); + if (f_property) + { + if (ue_py_convert_pyobject(value, f_property, self->u_struct_ptr, 0)) + { + return 0; + } + PyErr_SetString(PyExc_ValueError, "invalid value for FProperty"); + return -1; + } +#else UProperty *u_property = get_field_from_name(self->u_struct, (char *)attr); if (u_property) { @@ -214,6 +298,7 @@ static int ue_PyUScriptStruct_setattro(ue_PyUScriptStruct *self, PyObject *attr_ PyErr_SetString(PyExc_ValueError, "invalid value for UProperty"); return -1; } +#endif } return PyObject_GenericSetAttr((PyObject *)self, attr_name, value); } @@ -394,4 +479,4 @@ ue_PyUScriptStruct *py_ue_is_uscriptstruct(PyObject *obj) if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyUScriptStructType)) return nullptr; return (ue_PyUScriptStruct *)obj; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h index 1078cf686..9120640fe 100644 --- a/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h +++ b/Source/UnrealEnginePython/Private/UEPyUScriptStruct.h @@ -17,6 +17,10 @@ PyObject *py_ue_new_owned_uscriptstruct(UScriptStruct *, uint8 *); PyObject *py_ue_new_owned_uscriptstruct_zero_copy(UScriptStruct *, uint8 *); ue_PyUScriptStruct *py_ue_is_uscriptstruct(PyObject *); +#if ENGINE_MINOR_VERSION >= 25 +FProperty *ue_struct_get_field_from_name(UScriptStruct *, char *); +#else UProperty *ue_struct_get_field_from_name(UScriptStruct *, char *); +#endif -void ue_python_init_uscriptstruct(PyObject *); \ No newline at end of file +void ue_python_init_uscriptstruct(PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 3b2983b99..76ceaafb6 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -1,5 +1,9 @@ #include "UEPyObject.h" +#if ENGINE_MINOR_VERSION >= 25 +#include "UEPyProperty.h" +#endif + #include "PythonDelegate.h" #include "PythonFunction.h" #include "Components/ActorComponent.h" @@ -186,6 +190,15 @@ PyObject *py_ue_get_property_struct(ue_PyUObject * self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + FStructProperty *prop = CastField(f_property); + if (!prop) + return PyErr_Format(PyExc_Exception, "object is not a StructProperty"); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); @@ -193,6 +206,7 @@ PyObject *py_ue_get_property_struct(ue_PyUObject * self, PyObject * args) UStructProperty *prop = Cast(u_property); if (!prop) return PyErr_Format(PyExc_Exception, "object is not a StructProperty"); +#endif return py_ue_new_uscriptstruct(prop->Struct, prop->ContainerPtrToValuePtr(self->ue_object)); } @@ -357,7 +371,11 @@ PyObject *py_ue_post_edit_change_property(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *prop = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(prop_name))); +#else UProperty *prop = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(prop_name))); +#endif if (!prop) return PyErr_Format(PyExc_Exception, "unable to find property %s", prop_name); @@ -386,7 +404,11 @@ PyObject *py_ue_pre_edit_change(ue_PyUObject *self, PyObject * args) { ue_py_check(self); +#if ENGINE_MINOR_VERSION >= 25 + FProperty *prop = nullptr; +#else UProperty *prop = nullptr; +#endif char *prop_name = nullptr; if (!PyArg_ParseTuple(args, "|s:pre_edit_change", &prop_name)) @@ -482,6 +504,9 @@ PyObject *py_ue_get_metadata(ue_PyUObject * self, PyObject * args) return PyErr_Format(PyExc_TypeError, "the object does not support MetaData"); } +// as far as I can see metadata tags are only defined for UEditorAssetLibrary UObjects +// - other UObjects/FProperties just have metadata + PyObject *py_ue_get_metadata_tag(ue_PyUObject * self, PyObject * args) { @@ -801,6 +826,17 @@ PyObject *py_ue_set_property(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + + if (!ue_py_convert_pyobject(property_value, f_property, (uint8 *)self->ue_object, index)) + { + return PyErr_Format(PyExc_Exception, "unable to set property %s", property_name); + } +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); @@ -810,6 +846,7 @@ PyObject *py_ue_set_property(ue_PyUObject *self, PyObject * args) { return PyErr_Format(PyExc_Exception, "unable to set property %s", property_name); } +#endif Py_RETURN_NONE; @@ -838,14 +875,24 @@ PyObject *py_ue_set_property_flags(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); +#endif +#if ENGINE_MINOR_VERSION >= 25 + f_property->SetPropertyFlags((EPropertyFlags)flags); +#else #if ENGINE_MINOR_VERSION < 20 u_property->SetPropertyFlags(flags); #else u_property->SetPropertyFlags((EPropertyFlags)flags); +#endif #endif Py_RETURN_NONE; } @@ -873,15 +920,25 @@ PyObject *py_ue_add_property_flags(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); +#endif +#if ENGINE_MINOR_VERSION >= 25 + f_property->SetPropertyFlags(f_property->GetPropertyFlags() | (EPropertyFlags)flags); +#else #if ENGINE_MINOR_VERSION < 20 u_property->SetPropertyFlags(u_property->GetPropertyFlags() | flags); #else u_property->SetPropertyFlags(u_property->GetPropertyFlags() | (EPropertyFlags)flags); +#endif #endif Py_RETURN_NONE; } @@ -908,11 +965,19 @@ PyObject *py_ue_get_property_flags(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + return PyLong_FromUnsignedLong(f_property->GetPropertyFlags()); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); return PyLong_FromUnsignedLong(u_property->GetPropertyFlags()); +#endif } PyObject *py_ue_enum_values(ue_PyUObject *self, PyObject * args) @@ -994,6 +1059,15 @@ PyObject *py_ue_properties(ue_PyUObject *self, PyObject * args) PyObject *ret = PyList_New(0); +#if ENGINE_MINOR_VERSION >= 25 + for (TFieldIterator PropIt(u_struct); PropIt; ++PropIt) + { + FProperty* property = *PropIt; + PyObject *property_name = PyUnicode_FromString(TCHAR_TO_UTF8(*property->GetName())); + PyList_Append(ret, property_name); + Py_DECREF(property_name); + } +#else for (TFieldIterator PropIt(u_struct); PropIt; ++PropIt) { UProperty* property = *PropIt; @@ -1001,6 +1075,7 @@ PyObject *py_ue_properties(ue_PyUObject *self, PyObject * args) PyList_Append(ret, property_name); Py_DECREF(property_name); } +#endif return ret; } @@ -1078,11 +1153,19 @@ PyObject *py_ue_broadcast(ue_PyUObject *self, PyObject *args) const char *property_name = UEPyUnicode_AsUTF8(py_property_name); +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = self->ue_object->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find event property %s", property_name); + + if (auto casted_prop = CastField(f_property)) +#else UProperty *u_property = self->ue_object->GetClass()->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find event property %s", property_name); if (auto casted_prop = Cast(u_property)) +#endif { #if ENGINE_MINOR_VERSION >= 23 FMulticastScriptDelegate multiscript_delegate = *casted_prop->GetMulticastDelegate(self->ue_object); @@ -1095,9 +1178,15 @@ PyObject *py_ue_broadcast(ue_PyUObject *self, PyObject *args) uint32 argn = 1; // initialize args +#if ENGINE_MINOR_VERSION >= 25 + for (TFieldIterator IArgs(casted_prop->SignatureFunction); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) + { + FProperty *prop = *IArgs; +#else for (TFieldIterator IArgs(casted_prop->SignatureFunction); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) { UProperty *prop = *IArgs; +#endif if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) { prop->InitializeValue_InContainer(parms); @@ -1145,7 +1234,11 @@ PyObject *py_ue_broadcast(ue_PyUObject *self, PyObject *args) } else { +#if ENGINE_MINOR_VERSION >= 25 + return PyErr_Format(PyExc_Exception, "property is not a FMulticastDelegateProperty"); +#else return PyErr_Format(PyExc_Exception, "property is not a UMulticastDelegateProperty"); +#endif } Py_RETURN_NONE; @@ -1174,11 +1267,19 @@ PyObject *py_ue_get_property(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + return ue_py_convert_property(f_property, (uint8 *)self->ue_object, index); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); return ue_py_convert_property(u_property, (uint8 *)self->ue_object, index); +#endif } PyObject *py_ue_get_property_array_dim(ue_PyUObject *self, PyObject * args) @@ -1203,11 +1304,19 @@ PyObject *py_ue_get_property_array_dim(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + return PyLong_FromLongLong(f_property->ArrayDim); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); return PyLong_FromLongLong(u_property->ArrayDim); +#endif } #if WITH_EDITOR @@ -1270,6 +1379,51 @@ PyObject *py_ue_render_thumbnail(ue_PyUObject *self, PyObject * args) } #endif +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_fproperty(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + char *property_name; + if (!PyArg_ParseTuple(args, "s:get_fproperty", &property_name)) + { + return NULL; + } + + UStruct *u_struct = nullptr; + + if (self->ue_object->IsA()) + { + u_struct = (UStruct *)self->ue_object; + } + else + { + u_struct = (UStruct *)self->ue_object->GetClass(); + } + + // my current idea is that we need to return a python wrapper round FProperty now + // as FProperties are not UObjects + // currently going to assume that we dont need to handle garbage collection for FProperties + // because if I understand the rel notes right we should just use the simple c++ new + // and delete funcs + // but the question is it looks as tho for UObjects we have a single ue_PyUObject python + // wrapper stored in the housekeeper which we lookup by UObject address + // but for FProperties do we generate a python wrapper each time?? + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + //Py_RETURN_UOBJECT(u_property); + // so the above def is the following + //ue_PyUObject *ret = ue_get_python_uobject_inc(py_uobj);\ + //if (!ret)\ + // PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ + //return (PyObject *)ret; + + Py_RETURN_FPROPERTY(f_property); +} +#else PyObject *py_ue_get_uproperty(ue_PyUObject *self, PyObject * args) { @@ -1299,12 +1453,24 @@ PyObject *py_ue_get_uproperty(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(u_property); } +#endif PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) { ue_py_check(self); +#if ENGINE_MINOR_VERSION >= 25 + FArrayProperty *f_property = ue_py_check_type(self); + if (!f_property) + return PyErr_Format(PyExc_Exception, "object is not a FArrayProperty"); + + FProperty* inner = f_property->Inner; + if (!inner) + Py_RETURN_NONE; + + Py_RETURN_FPROPERTY(inner); +#else UArrayProperty *u_property = ue_py_check_type(self); if (!u_property) return PyErr_Format(PyExc_Exception, "object is not a UArrayProperty"); @@ -1314,6 +1480,7 @@ PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_UOBJECT(inner); +#endif } PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) @@ -1321,6 +1488,17 @@ PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) ue_py_check(self); +#if ENGINE_MINOR_VERSION >= 25 + FMapProperty *f_property = ue_py_check_type(self); + if (!f_property) + return PyErr_Format(PyExc_Exception, "object is not a FMapProperty"); + + FProperty* key = f_property->KeyProp; + if (!key) + Py_RETURN_NONE; + + Py_RETURN_FPROPERTY(key); +#else UMapProperty *u_property = ue_py_check_type(self); if (!u_property) return PyErr_Format(PyExc_Exception, "object is not a UMapProperty"); @@ -1330,6 +1508,7 @@ PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_UOBJECT(key); +#endif } PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) @@ -1337,6 +1516,17 @@ PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) ue_py_check(self); +#if ENGINE_MINOR_VERSION >= 25 + FMapProperty *f_property = ue_py_check_type(self); + if (!f_property) + return PyErr_Format(PyExc_Exception, "object is not a FMapProperty"); + + FProperty* value = f_property->ValueProp; + if (!value) + Py_RETURN_NONE; + + Py_RETURN_FPROPERTY(value); +#else UMapProperty *u_property = ue_py_check_type(self); if (!u_property) return PyErr_Format(PyExc_Exception, "object is not a UMapProperty"); @@ -1346,6 +1536,7 @@ PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_UOBJECT(value); +#endif } PyObject *py_ue_has_property(ue_PyUObject *self, PyObject * args) @@ -1370,9 +1561,15 @@ PyObject *py_ue_has_property(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + Py_RETURN_FALSE; +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) Py_RETURN_FALSE; +#endif Py_RETURN_TRUE; } @@ -1398,11 +1595,26 @@ PyObject *py_ue_get_property_class(ue_PyUObject *self, PyObject * args) u_struct = (UStruct *)self->ue_object->GetClass(); } +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); + + // this now has to be an FFieldClass - we are returning the class of the property + // NOT the class of the contained property + // similar to below which would return eg UIntProperty etc + // so I think we have a problem - whereas for UObjects the class is UClass which is also + // a UObject - for FProperties the class is FFieldClass so we need a wrap of FProperty + // and FFieldClass - which means we need a different function for returning the + // python wrap of FProperty and the python wrap of an FProperties class + Py_RETURN_FFIELDCLASS(f_property->GetClass()); +#else UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(property_name))); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", property_name); Py_RETURN_UOBJECT(u_property->GetClass()); +#endif } @@ -1551,6 +1763,15 @@ PyObject *py_ue_delegate_bind_ufunction(ue_PyUObject * self, PyObject * args) if (!PyArg_ParseTuple(args, "sOs:delegate_bind_ufunction", &delegate_name, &py_obj, &fname)) return nullptr; +#if ENGINE_MINOR_VERSION >= 25 + FProperty *f_property = self->ue_object->GetClass()->FindPropertyByName(FName(delegate_name)); + if (!f_property) + return PyErr_Format(PyExc_Exception, "unable to find property %s", delegate_name); + + FDelegateProperty *Prop = CastField(f_property); + if (!Prop) + return PyErr_Format(PyExc_Exception, "property is not a FDelegateProperty"); +#else UProperty *u_property = self->ue_object->GetClass()->FindPropertyByName(FName(delegate_name)); if (!u_property) return PyErr_Format(PyExc_Exception, "unable to find property %s", delegate_name); @@ -1558,6 +1779,7 @@ PyObject *py_ue_delegate_bind_ufunction(ue_PyUObject * self, PyObject * args) UDelegateProperty *Prop = Cast(u_property); if (!Prop) return PyErr_Format(PyExc_Exception, "property is not a UDelegateProperty"); +#endif UObject *Object = ue_py_check_type(py_obj); if (!Object) @@ -1607,6 +1829,444 @@ PyObject *py_ue_add_function(ue_PyUObject * self, PyObject * args) } #endif +#if ENGINE_MINOR_VERSION >= 25 +// so I think we have to completely rewrite this for 4.25 +PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) +{ + // so this routine appears to be dynamically adding a new property to an existing + // UObject in Python + // - nothing to do with accessing or setting properties - thats the ue_PyUObject getttro/setattro functions + + ue_py_check(self); + + PyObject *obj; + char *name; + PyObject *property_class = nullptr; + PyObject *property_class2 = nullptr; + if (!PyArg_ParseTuple(args, "Os|OO:add_property", &obj, &name, &property_class, &property_class2)) + { + return NULL; + } + + if (!self->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a UStruct"); + + // so scope defines the property container subclass of container properties eg array or map + // for FProperties it apparently is an FFieldVariant ie either a UObject or FField + // fproperty is the property class of non-container properties or the item property class for + // array properties or key property class for map containers + // fproperty2 defines the value property class for map containers + + // u_prop_class defines the UObject class of property values, the item UOBject class for array + // properties or the key UObject class for map properties + // u_prop_class2 defines the UObject class of value property values for map properties + + // this is not a UObject anymore but not clear what replacement is + // this should be an FFieldVariant into which we can store either a UObject or an FField + //UObject *scope = nullptr; + FFieldVariant scope; + FFieldVariant *scopeptr = nullptr; + + FProperty *f_property = nullptr; + FFieldClass *f_class = nullptr; + FProperty *f_property2 = nullptr; + FFieldClass *f_class2 = nullptr; + + UClass *u_prop_class = nullptr; + UScriptStruct *u_script_struct = nullptr; + UClass *u_prop_class2 = nullptr; + UScriptStruct *u_script_struct2 = nullptr; + bool is_array = false; + bool is_map = false; + + // so what to do here + // this is going to need exploration + // as these are describing the class of the property value I think these are still going + // to be some version of UObject + + if (property_class) + { + if (!ue_is_pyuobject(property_class)) + { + return PyErr_Format(PyExc_Exception, "property class arg is not a uobject"); + } + ue_PyUObject *py_prop_class = (ue_PyUObject *)property_class; + if (py_prop_class->ue_object->IsA()) + { + u_prop_class = (UClass *)py_prop_class->ue_object; + } + else if (py_prop_class->ue_object->IsA()) + { + u_script_struct = (UScriptStruct *)py_prop_class->ue_object; + } + else + { + return PyErr_Format(PyExc_Exception, "property class arg is not a UClass or a UScriptStruct"); + } + } + + if (property_class2) + { + if (!ue_is_pyuobject(property_class2)) + { + return PyErr_Format(PyExc_Exception, "secondary property class arg is not a uobject"); + } + ue_PyUObject *py_prop_class = (ue_PyUObject *)property_class2; + if (py_prop_class->ue_object->IsA()) + { + u_prop_class2 = (UClass *)py_prop_class->ue_object; + } + else if (py_prop_class->ue_object->IsA()) + { + u_script_struct2 = (UScriptStruct *)py_prop_class->ue_object; + } + else + { + return PyErr_Format(PyExc_Exception, "secondary property class arg is not a UClass or a UScriptStruct"); + } + } + + EObjectFlags o_flags = RF_Public | RF_MarkAsNative;// | RF_Transient; + + if (ue_is_pyuobject(obj)) + { + // complication here - a uobject can NOT be an FProperty any more + // what I think this may be doing is seeing if the passed UObject was some form of UProperty + // except that here it MUST be a UProperty subclass + // so apparently this object was expected to be a UProperty subclass of some description + // so currently think the thing to do here is to exit + return PyErr_Format(PyExc_Exception, "object is a uobject - it must be an fproperty subclass"); + //ue_PyUObject *py_obj = (ue_PyUObject *)obj; + //if (!py_obj->ue_object->IsA()) + //{ + // return PyErr_Format(PyExc_Exception, "uobject is not a UClass"); + //} + //u_class = (UClass *)py_obj->ue_object; + //if (!u_class->IsChildOf()) + // return PyErr_Format(PyExc_Exception, "uobject is not a UProperty"); + //if (u_class == UArrayProperty::StaticClass()) + // return PyErr_Format(PyExc_Exception, "please use a single-item list of property for arrays"); + //scope = self->ue_object; + } + else if (ue_is_pyfproperty(obj)) + { + // I think all this is doing is allowing a new property to be defined in python + // by passing an FProperty subclass python object + + // so now think this is wrong also - we need a class here which is now a different c++ class + // from FProperty ie FFieldClass + return PyErr_Format(PyExc_Exception, "object is an fproperty - it must be an ffieldclass subclass"); + + // so far this seems to be referring to the Property class + //ue_PyFProperty *py_obj = (ue_PyFProperty *)obj; + //if (!py_obj->ue_fproperty->IsA(FFieldClass)) + //{ + // return PyErr_Format(PyExc_Exception, "fproperty is not an FFieldClass ie an FProperty subclass class object"); + //} + //f_class = (FFieldClass *)py_obj->ue_fproperty; + // this is sort of by definition true now + //if (!f_class->IsChildOf(FProperty::StaticClass())) + // return PyErr_Format(PyExc_Exception, "fproperty is not an FProperty"); + //if (f_class == FArrayProperty::StaticClass()) + // return PyErr_Format(PyExc_Exception, "please use a single-item list of property for arrays"); + // so scope should be the owning object we are creating the property for ie a uobject + //scope = FFieldVariant(self->ue_object); + //scopeptr = &scope; + } + else if (ue_is_pyffieldclass(obj)) + { + // I think all this is doing is allowing a new property to be defined in python + // by passing an FProperty subclass python object + + // so far this seems to be referring to the Property class + ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)obj; + // by definition this is a FFieldClass object + //if (!py_obj->ue_ffieldclass->IsA(FFieldClass)) + //{ + // return PyErr_Format(PyExc_Exception, "fproperty is not an FFieldClass ie an FProperty subclass class object"); + //} + //f_class = (FFieldClass *)py_obj->ue_ffieldclass; + f_class = py_obj->ue_ffieldclass; + // this is sort of by definition true now + if (!f_class->IsChildOf(FProperty::StaticClass())) + return PyErr_Format(PyExc_Exception, "fproperty is not an FProperty"); + if (f_class == FArrayProperty::StaticClass()) + return PyErr_Format(PyExc_Exception, "please use a single-item list of property for arrays"); + // so scope should be the owning object we are creating the property for ie a uobject + scope = FFieldVariant(self->ue_object); + scopeptr = &scope; + } + else if (PyList_Check(obj)) + { + if (PyList_Size(obj) == 1) + { + PyObject *py_item = PyList_GetItem(obj, 0); + //if (ue_is_pyfproperty(py_item)) + if (ue_is_pyffieldclass(py_item)) + { + //ue_PyFProperty *py_obj = (ue_PyFProperty *)py_item; + //if (!py_obj->ue_fproperty->IsA(FFieldClass)) + //{ + // return PyErr_Format(PyExc_Exception, "array property item fproperty is not an FFieldClass ie an FProperty subclass class object"); + //} + //ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)py_item; + //f_class = (FFieldClass *)py_obj->ue_fproperty; + ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)py_item; + f_class = py_obj->ue_ffieldclass; + if (!f_class->IsChildOf(FProperty::StaticClass())) + return PyErr_Format(PyExc_Exception, "array property item fproperty is not a FProperty"); + if (f_class == FArrayProperty::StaticClass()) + return PyErr_Format(PyExc_Exception, "please use a single-item list of property for arrays"); + FArrayProperty *f_array = new FArrayProperty(self->ue_object, UTF8_TO_TCHAR(name), o_flags); + if (!f_array) + return PyErr_Format(PyExc_Exception, "unable to allocate new FProperty"); + scope = FFieldVariant(f_array); + scopeptr = &scope; + is_array = true; + } + Py_DECREF(py_item); + } + else if (PyList_Size(obj) == 2) + { + PyObject *py_key = PyList_GetItem(obj, 0); + PyObject *py_value = PyList_GetItem(obj, 1); + //if (ue_is_pyfproperty(py_key) && ue_is_pyfproperty(py_value)) + if (ue_is_pyffieldclass(py_key) && ue_is_pyffieldclass(py_value)) + { + // KEY + //ue_PyFProperty *py_obj = (ue_PyFProperty *)py_key; + //if (!py_obj->ue_fproperty->IsA(FFieldClass)) + //{ + // return PyErr_Format(PyExc_Exception, "map property key fproperty is not an FFieldClass ie an FProperty subclass class object"); + //} + //f_class = (FFieldClass *)py_obj->ue_fproperty; + ue_PyFFieldClass *py_obj = (ue_PyFFieldClass *)py_key; + f_class = py_obj->ue_ffieldclass; + if (!f_class->IsChildOf(FProperty::StaticClass())) + return PyErr_Format(PyExc_Exception, "map property key fproperty is not a FProperty"); + if (f_class == FArrayProperty::StaticClass()) + return PyErr_Format(PyExc_Exception, "please use a two-items list of properties for maps"); + + // VALUE + //ue_PyFProperty *py_obj2 = (ue_PyFProperty *)py_value; + //if (!py_obj2->ue_fproperty->IsA(FFieldClass)) + //{ + // return PyErr_Format(PyExc_Exception, "map property value fproperty is not an FFieldClass ie an FProperty subclass class object"); + //} + //f_class2 = (FFieldClass *)py_obj2->ue_fproperty; + ue_PyFFieldClass *py_obj2 = (ue_PyFFieldClass *)py_key; + f_class2 = py_obj2->ue_ffieldclass; + if (!f_class2->IsChildOf(FProperty::StaticClass())) + return PyErr_Format(PyExc_Exception, "map property value fproperty is not a FProperty"); + if (f_class2 == FArrayProperty::StaticClass()) + return PyErr_Format(PyExc_Exception, "please use a two-items list of properties for maps"); + + + FMapProperty *f_map = new FMapProperty(self->ue_object, UTF8_TO_TCHAR(name), o_flags); + if (!f_map) + return PyErr_Format(PyExc_Exception, "unable to allocate new FProperty"); + scope = FFieldVariant(f_map); + scopeptr = &scope; + is_map = true; + } + Py_DECREF(py_key); + Py_DECREF(py_value); + } + } + + + if (!scope) + { + return PyErr_Format(PyExc_Exception, "argument is not an FProperty subclass or a single item list"); + } + + // so for NewObject the 1st argument scope is the outer uobject + // - this does not exist for FProperties - its now the owner object + //u_property = NewObject(scope, u_class, UTF8_TO_TCHAR(name), o_flags); + + // this is all wrong - not clear how to do this yet + // but we do not allocate FProperties with NewObject + // however we need a new function because the following is not a valid FProperty constructor + // for the moment looks as tho need to explicitly allocate each FProperty type + // so scope is the owner object which is apparently an FFieldVarient ie it can hold + // either a UObject or an FField + //f_property = new FProperty(scope, u_class, UTF8_TO_TCHAR(name), o_flags); + f_property = FProperty_New(&scope, f_class, FName(UTF8_TO_TCHAR(name)), o_flags); + if (!f_property) + { + // MarkPendingKill is a UObject feature + // NEEDS FIXING + // or does it - maybe taken care of by the destructor of FArrayProperty or FMapProperty + //if (is_array || is_map) + // scope->MarkPendingKill(); + return PyErr_Format(PyExc_Exception, "unable to allocate new FProperty"); + } + + // one day we may want to support transient properties... + //uint64 flags = CPF_Edit | CPF_BlueprintVisible | CPF_Transient | CPF_ZeroConstructor; + uint64 flags = CPF_Edit | CPF_BlueprintVisible | CPF_ZeroConstructor; + + // we assumed Actor Components to be non-editable + if (u_prop_class && u_prop_class->IsChildOf()) + { + flags |= ~CPF_Edit; + } + + // TODO manage replication + /* + if (replicate && PyObject_IsTrue(replicate)) { + flags |= CPF_Net; + }*/ + + if (is_array) + { + FArrayProperty *f_array = (FArrayProperty *)scope.ToField(); + f_array->AddCppProperty(f_property); + f_property->SetPropertyFlags((EPropertyFlags)flags); + if (f_property->GetClass() == FObjectProperty::StaticClass()) + { + FObjectProperty *obj_prop = (FObjectProperty *)f_property; + if (u_prop_class) + { + obj_prop->SetPropertyClass(u_prop_class); + } + } + if (f_property->GetClass() == FStructProperty::StaticClass()) + { + FStructProperty *obj_prop = (FStructProperty *)f_property; + if (u_script_struct) + { + obj_prop->Struct = u_script_struct; + } + } + f_property = f_array; + } + + if (is_map) + { + //u_property2 = NewObject(scope, u_class2, NAME_None, o_flags); + f_property2 = FProperty_New(&scope, f_class2, FName(UTF8_TO_TCHAR(name)), o_flags); + if (!f_property2) + { + // MarkPendingKill is a UObject feature + // NEEDS FIXING + // or does it - maybe taken care of by the destructor of FArrayProperty or FMapProperty + //if (is_array || is_map) + // scope->MarkPendingKill(); + return PyErr_Format(PyExc_Exception, "unable to allocate new FProperty"); + } + FMapProperty *f_map = (FMapProperty *)scope.ToField(); + + f_property->SetPropertyFlags((EPropertyFlags)flags); + f_property2->SetPropertyFlags((EPropertyFlags)flags); + + if (f_property->GetClass() == FObjectProperty::StaticClass()) + { + FObjectProperty *obj_prop = (FObjectProperty *)f_property; + if (u_prop_class) + { + obj_prop->SetPropertyClass(u_prop_class); + } + } + if (f_property->GetClass() == FStructProperty::StaticClass()) + { + FStructProperty *obj_prop = (FStructProperty *)f_property; + if (u_script_struct) + { + obj_prop->Struct = u_script_struct; + } + } + + if (f_property2->GetClass() == FObjectProperty::StaticClass()) + { + FObjectProperty *obj_prop = (FObjectProperty *)f_property2; + if (u_prop_class2) + { + obj_prop->SetPropertyClass(u_prop_class2); + } + } + if (f_property2->GetClass() == FStructProperty::StaticClass()) + { + FStructProperty *obj_prop = (FStructProperty *)f_property2; + if (u_script_struct2) + { + obj_prop->Struct = u_script_struct2; + } + } + + f_map->KeyProp = f_property; + f_map->ValueProp = f_property2; + + f_property = f_map; + } + + if (f_class == FMulticastDelegateProperty::StaticClass()) + { + FMulticastDelegateProperty *mcp = (FMulticastDelegateProperty *)f_property; + mcp->SignatureFunction = NewObject(self->ue_object, NAME_None, RF_Public | RF_Transient | RF_MarkAsNative); + mcp->SignatureFunction->FunctionFlags = FUNC_MulticastDelegate | FUNC_BlueprintCallable | FUNC_Native; + flags |= CPF_BlueprintAssignable | CPF_BlueprintCallable; + flags &= ~CPF_Edit; + } + + else if (f_class == FDelegateProperty::StaticClass()) + { + FDelegateProperty *udp = (FDelegateProperty *)f_property; + udp->SignatureFunction = NewObject(self->ue_object, NAME_None, RF_Public | RF_Transient | RF_MarkAsNative); + udp->SignatureFunction->FunctionFlags = FUNC_MulticastDelegate | FUNC_BlueprintCallable | FUNC_Native; + flags |= CPF_BlueprintAssignable | CPF_BlueprintCallable; + flags &= ~CPF_Edit; + } + + else if (f_class == FObjectProperty::StaticClass()) + { + // ensure it is not an arry as we have already managed it ! + if (!is_array && !is_map) + { + FObjectProperty *obj_prop = (FObjectProperty *)f_property; + if (u_prop_class) + { + obj_prop->SetPropertyClass(u_prop_class); + } + } + } + + else if (f_class == FStructProperty::StaticClass()) + { + // ensure it is not an arry as we have already managed it ! + if (!is_array && !is_map) + { + FStructProperty *obj_prop = (FStructProperty *)f_property; + if (u_script_struct) + { + obj_prop->Struct = u_script_struct; + } + } + } + + f_property->SetPropertyFlags((EPropertyFlags)flags); + f_property->ArrayDim = 1; + + // so is this how we add properties?? + UStruct *u_struct = (UStruct *)self->ue_object; + u_struct->AddCppProperty(f_property); + u_struct->StaticLink(true); + + + if (u_struct->IsA()) + { + UClass *owner_class = (UClass *)u_struct; + owner_class->GetDefaultObject()->RemoveFromRoot(); + owner_class->GetDefaultObject()->ConditionalBeginDestroy(); + owner_class->ClassDefaultObject = nullptr; + owner_class->GetDefaultObject()->PostInitProperties(); + } + + // TODO add default value + + Py_RETURN_FPROPERTY(f_property); +} +#else PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) { @@ -1953,6 +2613,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) Py_RETURN_UOBJECT(u_property); } +#endif PyObject *py_ue_as_dict(ue_PyUObject * self, PyObject * args) { @@ -1977,7 +2638,11 @@ PyObject *py_ue_as_dict(ue_PyUObject * self, PyObject * args) } PyObject *py_struct_dict = PyDict_New(); +#if ENGINE_MINOR_VERSION >= 25 + TFieldIterator SArgs(u_struct); +#else TFieldIterator SArgs(u_struct); +#endif for (; SArgs; ++SArgs) { PyObject *struct_value = ue_py_convert_property(*SArgs, (uint8 *)u_object, 0); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index 6fb4678fe..5afe1a8ea 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -24,7 +24,11 @@ PyObject *py_ue_properties(ue_PyUObject *, PyObject *); PyObject *py_ue_call(ue_PyUObject *, PyObject *); PyObject *py_ue_get_property(ue_PyUObject *, PyObject *); PyObject *py_ue_get_property_array_dim(ue_PyUObject *, PyObject *); +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_fproperty(ue_PyUObject *, PyObject *); +#else PyObject *py_ue_get_uproperty(ue_PyUObject *, PyObject *); +#endif PyObject *py_ue_get_inner(ue_PyUObject *, PyObject *); PyObject *py_ue_get_key_prop(ue_PyUObject *, PyObject *); PyObject *py_ue_get_value_prop(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp new file mode 100644 index 000000000..e0457d69e --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp @@ -0,0 +1,216 @@ + +#include "UEPyProperty.h" + +#if ENGINE_MINOR_VERSION >= 25 + +#include "PythonDelegate.h" +#include "PythonFunction.h" +#include "Components/ActorComponent.h" +#include "Engine/UserDefinedEnum.h" + +#if WITH_EDITOR +#include "Runtime/AssetRegistry/Public/AssetRegistryModule.h" +#include "ObjectTools.h" +#include "UnrealEd.h" +#include "Runtime/Core/Public/HAL/FeedbackContextAnsi.h" + +#include "Wrappers/UEPyFObjectThumbnail.h" +#endif + +#include "Runtime/Core/Public/Misc/OutputDeviceNull.h" +#include "Runtime/CoreUObject/Public/Serialization/ObjectWriter.h" +#include "Runtime/CoreUObject/Public/Serialization/ObjectReader.h" + + + +PyObject *get_fproperty_uclass(FProperty *property) +{ + UClass *u_class = nullptr; + //ue_py_check(self); + + // why was I thinking this - not clear what I was trying to do here + // return the UClass for an FProperty + // or return UClass for a python wrapped FProperty?? + // obviously the return seems to be a python wrapped UClass object + + // not clear how to implement this + // we may just have to check each FProperty class + // and load the StaticClass of the appropriate UObject + + //u_class = (UClass *)property->ue_fproperty + + Py_RETURN_UOBJECT(u_class); +} + + +FProperty* FProperty_New(FFieldVariant* scope, FFieldClass* f_class, FName name, EObjectFlags o_flags) +{ + //FArrayProperty *f_array = new FArrayProperty(self->ue_object, UTF8_TO_TCHAR(name), o_flags); + + // for the moment lets just do explicit ifs + if (f_class == FBoolProperty::StaticClass()) + { + FBoolProperty *f_prop = new FBoolProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FIntProperty::StaticClass()) + { + FIntProperty *f_prop = new FIntProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FUInt32Property::StaticClass()) + { + FUInt32Property *f_prop = new FUInt32Property(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FInt64Property::StaticClass()) + { + FInt64Property *f_prop = new FInt64Property(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FUInt64Property::StaticClass()) + { + FUInt64Property *f_prop = new FUInt64Property(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FFloatProperty::StaticClass()) + { + FFloatProperty *f_prop = new FFloatProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FByteProperty::StaticClass()) + { + FByteProperty *f_prop = new FByteProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FEnumProperty::StaticClass()) + { + FEnumProperty *f_prop = new FEnumProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FStrProperty::StaticClass()) + { + FStrProperty *f_prop = new FStrProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FNameProperty::StaticClass()) + { + FNameProperty *f_prop = new FNameProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FTextProperty::StaticClass()) + { + FTextProperty *f_prop = new FTextProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FArrayProperty::StaticClass()) + { + FArrayProperty *f_prop = new FArrayProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FMapProperty::StaticClass()) + { + FMapProperty *f_prop = new FMapProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FStructProperty::StaticClass()) + { + FStructProperty *f_prop = new FStructProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FClassProperty::StaticClass()) + { + FClassProperty *f_prop = new FClassProperty(*scope, name, o_flags); + return f_prop; + } + else if (f_class == FObjectPropertyBase::StaticClass()) + { + FObjectPropertyBase *f_prop = new FObjectPropertyBase(*scope, name, o_flags); + return f_prop; + } + + UE_LOG(LogPython, Error, TEXT("new property unimplemented property class %s for %s"), TCHAR_TO_UTF8(*f_class->GetName()), *name.ToString()); + + return nullptr; +} + +PyObject *py_ue_fproperty_set_metadata(ue_PyFProperty * self, PyObject * args) +{ + + // not clear what to do here - we can have a valid python uobject wrapper + // with invalid uobject - which is what ue_py_check is testing for + // can we have a valid python fproperty wrapper with invalid fproperty?? + //ue_py_check(self); + //if (!ue_is_pyfproperty(self)) + // return PyErr_Format(PyExc_Exception, "fproperty_set_metadata: self is not FProperty"); + if (self->ue_fproperty == nullptr) + return PyErr_Format(PyExc_Exception, "PyFProperty is in invalid state"); + + char *metadata_key; + char *metadata_value; + if (!PyArg_ParseTuple(args, "ss:set_metadata", &metadata_key, &metadata_value)) + { + return NULL; + } + + FProperty *f_property = (FProperty *)self->ue_fproperty; + f_property->SetMetaData(FName(UTF8_TO_TCHAR(metadata_key)), UTF8_TO_TCHAR(metadata_value)); + + Py_RETURN_NONE; +} + +PyObject *py_ue_fproperty_get_metadata(ue_PyFProperty * self, PyObject * args) +{ + + // not clear what to do here - we can have a valid python uobject wrapper + // with invalid uobject - which is what ue_py_check is testing for + // can we have a valid python fproperty wrapper with invalid fproperty?? + //ue_py_check(self); + //if (!ue_is_pyfproperty(self)) + // return PyErr_Format(PyExc_Exception, "fproperty_get_metadata: self is not FProperty"); + if (self->ue_fproperty == nullptr) + return PyErr_Format(PyExc_Exception, "PyFProperty is in invalid state"); + + char *metadata_key; + if (!PyArg_ParseTuple(args, "s:get_metadata", &metadata_key)) + { + return NULL; + } + + char *metadata_value = nullptr; + + FProperty *f_property = (FProperty *)self->ue_fproperty; + FString value = f_property->GetMetaData(FName(UTF8_TO_TCHAR(metadata_key))); + return PyUnicode_FromString(TCHAR_TO_UTF8(*value)); + +} + +PyObject *py_ue_fproperty_has_metadata(ue_PyFProperty * self, PyObject * args) +{ + + // not clear what to do here - we can have a valid python uobject wrapper + // with invalid uobject - which is what ue_py_check is testing for + // can we have a valid python fproperty wrapper with invalid fproperty?? + //ue_py_check(self); + //if (!ue_is_pyfproperty(self)) + // return PyErr_Format(PyExc_Exception, "fproperty_has_metadata: self is not FProperty"); + if (self->ue_fproperty == nullptr) + return PyErr_Format(PyExc_Exception, "PyFProperty is in invalid state"); + + char *metadata_key; + if (!PyArg_ParseTuple(args, "s:has_metadata", &metadata_key)) + { + return NULL; + } + + FProperty *f_property = (FProperty *)self->ue_fproperty; + if (f_property->HasMetaData(FName(UTF8_TO_TCHAR(metadata_key)))) + { + Py_INCREF(Py_True); + return Py_True; + } + Py_INCREF(Py_False); + return Py_False; +} + +#endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h new file mode 100644 index 000000000..09c8f7a01 --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h @@ -0,0 +1,14 @@ +#pragma once + + + +#include "UEPyModule.h" + +#if ENGINE_MINOR_VERSION >= 25 +PyObject *get_fproperty_uclass(FProperty *); +FProperty* FProperty_New(FFieldVariant* scope, FFieldClass* f_class, FName name, EObjectFlags o_flags); +PyObject *py_ue_fproperty_set_metadata(ue_PyFProperty * self, PyObject * args); +PyObject *py_ue_fproperty_get_metadata(ue_PyFProperty * self, PyObject * args); +PyObject *py_ue_fproperty_has_metadata(ue_PyFProperty * self, PyObject * args); +#endif + diff --git a/Source/UnrealEnginePython/Public/UnrealEnginePython.h b/Source/UnrealEnginePython/Public/UnrealEnginePython.h index 1806609f0..017887386 100644 --- a/Source/UnrealEnginePython/Public/UnrealEnginePython.h +++ b/Source/UnrealEnginePython/Public/UnrealEnginePython.h @@ -9,6 +9,17 @@ //#define UEPY_MEMORY_DEBUG 1 +//#define EXTRA_LOGGING 1 + +#ifdef EXTRA_LOGGING +#define EXTRA_UE_LOG(log_python, log_level, fmt, args...) \ + UE_LOG(log_python, log_level, fmt, ##args) +#define EXTRA_DEBUG_CODE 1 +#else +#define EXTRA_UE_LOG(...) +#endif + + #include "CoreMinimal.h" #include "Runtime/Core/Public/Modules/ModuleManager.h" #include "Runtime/SlateCore/Public/Styling/SlateStyle.h" @@ -86,6 +97,67 @@ UNREALENGINEPYTHON_API ue_PyUObject *ue_get_python_uobject_inc(UObject *); return PyErr_Format(PyExc_Exception, "uobject is in invalid state");\ return (PyObject *)ret; +#if ENGINE_MINOR_VERSION >= 25 + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + FProperty *ue_fproperty; + // the __dict__ + PyObject *py_dict; + //// if true RemoveFromRoot will be called at object destruction time + //int auto_rooted; + //// if owned the life of the UObject is related to the life of PyObject + //int owned; +} ue_PyFProperty; + +UNREALENGINEPYTHON_API ue_PyFProperty *ue_get_python_fproperty(FProperty *); +UNREALENGINEPYTHON_API ue_PyFProperty *ue_get_python_fproperty_inc(FProperty *); + +#define Py_RETURN_FPROPERTY(py_fprop) ue_PyFProperty *ret = ue_get_python_fproperty_inc(py_fprop);\ + if (!ret)\ + return PyErr_Format(PyExc_Exception, "fproperty is invalid");\ + return (PyObject *)ret; + +#define Py_RETURN_FPROPERTY_NOINC(py_fprop) ue_PyFProperty *ret = ue_get_python_fproperty(py_fprop);\ + if (!ret)\ + return PyErr_Format(PyExc_Exception, "fproperty is invalid");\ + return (PyObject *)ret; + +// for the moment it seems I need both an FProperty wrap and an FFieldClass wrap +// not sure about this yet +// for uobjects both property and class are the same type ie uobjects which is not true for +// FProperties but havent figured the ramifications of this yet + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + FFieldClass *ue_ffieldclass; + // the __dict__ + PyObject *py_dict; + //// if true RemoveFromRoot will be called at object destruction time + //int auto_rooted; + //// if owned the life of the UObject is related to the life of PyObject + //int owned; +} ue_PyFFieldClass; + +UNREALENGINEPYTHON_API ue_PyFFieldClass *ue_get_python_ffieldclass(FFieldClass *); +UNREALENGINEPYTHON_API ue_PyFFieldClass *ue_get_python_ffieldclass_inc(FFieldClass *); + +#define Py_RETURN_FFIELDCLASS(py_ffieldclass) ue_PyFFieldClass *ret = ue_get_python_ffieldclass_inc(py_ffieldclass);\ + if (!ret)\ + return PyErr_Format(PyExc_Exception, "ffieldclass is invalid");\ + return (PyObject *)ret; + +#define Py_RETURN_FFIELDCLASS_NOINC(py_ffieldclass) ue_PyFFieldClass *ret = ue_get_python_ffieldclass(py_ffieldclass);\ + if (!ret)\ + return PyErr_Format(PyExc_Exception, "ffieldclass is invalid");\ + return (PyObject *)ret; + +#endif + #if ENGINE_MINOR_VERSION < 16 template struct TStructOpsTypeTraitsBase2 : TStructOpsTypeTraitsBase From 98e31d12cd80d0680148d09e0e0f66015b080dfc Mon Sep 17 00:00:00 2001 From: David Date: Mon, 29 Jun 2020 08:24:35 +0100 Subject: [PATCH 18/31] Added debug prints under define EXTRA_UE_LOG control. --- .../UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp index 91b62ea39..544fc6083 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp @@ -316,6 +316,7 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) { return PyErr_Format(PyExc_Exception, "argument is not a child of UEdGraphNode"); } + EXTRA_UE_LOG(LogPython, Warning, TEXT("add_node: Casting to class %s"), *u_class->GetName()); node = NewObject(graph, u_class); node->PostLoad(); } @@ -328,6 +329,7 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) node->Rename(*node->GetName(), graph); } + EXTRA_UE_LOG(LogPython, Warning, TEXT("add_node: renaming node")); } if (!node) @@ -337,8 +339,10 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) node->CreateNewGuid(); node->PostPlacedNewNode(); node->SetFlags(RF_Transactional); + EXTRA_UE_LOG(LogPython, Warning, TEXT("add_node: allocate pins current pins num %d"), node->Pins.Num()); if (node->Pins.Num() == 0) { + EXTRA_UE_LOG(LogPython, Warning, TEXT("add_node: allocating pins")); node->AllocateDefaultPins(); } node->NodePosX = x; @@ -527,6 +531,8 @@ PyObject *py_ue_graph_add_node_dynamic_cast(ue_PyUObject * self, PyObject * args if(!u_class) return PyErr_Format(PyExc_Exception, "argument is not a UClass"); + EXTRA_UE_LOG(LogPython, Warning, TEXT("add_node_dynamic_cast: Casting to class %s %p"), *u_class->GetName(), (void *)u_class); + UK2Node_DynamicCast *node = NewObject(graph); node->TargetType = u_class; #if ENGINE_MINOR_VERSION > 15 @@ -555,6 +561,8 @@ PyObject *py_ue_graph_add_node_dynamic_cast(ue_PyUObject * self, PyObject * args FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); } + EXTRA_UE_LOG(LogPython, Warning, TEXT("add_node_dynamic_cast: targettype is %p"), (void *)(node->TargetType)); + Py_RETURN_UOBJECT(node); } From 7f9b91793078fc4afb9a15af6e490af95bdde195 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 29 Jun 2020 09:14:05 +0100 Subject: [PATCH 19/31] Added python3.8 for Linux as that is default for Ubuntu 20.04. --- .../UnrealEnginePython/UnrealEnginePython.Build.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index ee8070ba7..ceb49f31d 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -39,6 +39,8 @@ public class UnrealEnginePython : ModuleRules private string[] linuxKnownIncludesPaths = { + "/usr/local/include/python3.8", + "/usr/local/include/python3.8m", "/usr/local/include/python3.7", "/usr/local/include/python3.7m", "/usr/local/include/python3.6", @@ -46,6 +48,8 @@ public class UnrealEnginePython : ModuleRules "/usr/local/include/python3.5", "/usr/local/include/python3.5m", "/usr/local/include/python2.7", + "/usr/include/python3.8", + "/usr/include/python3.8m", "/usr/include/python3.7", "/usr/include/python3.7m", "/usr/include/python3.6", @@ -57,6 +61,10 @@ public class UnrealEnginePython : ModuleRules private string[] linuxKnownLibsPaths = { + "/usr/local/lib/libpython3.8.so", + "/usr/local/lib/libpython3.8m.so", + "/usr/local/lib/x86_64-linux-gnu/libpython3.8.so", + "/usr/local/lib/x86_64-linux-gnu/libpython3.8m.so", "/usr/local/lib/libpython3.7.so", "/usr/local/lib/libpython3.7m.so", "/usr/local/lib/x86_64-linux-gnu/libpython3.7.so", @@ -71,6 +79,10 @@ public class UnrealEnginePython : ModuleRules "/usr/local/lib/x86_64-linux-gnu/libpython3.5m.so", "/usr/local/lib/libpython2.7.so", "/usr/local/lib/x86_64-linux-gnu/libpython2.7.so", + "/usr/lib/libpython3.8.so", + "/usr/lib/libpython3.8m.so", + "/usr/lib/x86_64-linux-gnu/libpython3.8.so", + "/usr/lib/x86_64-linux-gnu/libpython3.8m.so", "/usr/lib/libpython3.7.so", "/usr/lib/libpython3.7m.so", "/usr/lib/x86_64-linux-gnu/libpython3.7.so", From 21b4ddc2a8414a4a2b3ab0b50a4aba6a547c3c97 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 29 Jun 2020 09:15:11 +0100 Subject: [PATCH 20/31] Fixup for building on Ubuntu 20.04. Without this fails with x86_64-linux-gnu/python3.8/pyconfig.h file not found. Need to create Include directory in Source/UnrealEnginePython which contains an x86_64-linux-gnu directory containing symbolic link to /usr/include/x86_64-linux-gnu/pythonx.x where x.x is appropriate python version. Directly including /usr/include/x86_64-linux-gnu/pythonx.x in linuxKnownIncludesPaths does not work. --- Source/UnrealEnginePython/UnrealEnginePython.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index ceb49f31d..0aea1acfd 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -259,6 +259,7 @@ public UnrealEnginePython(TargetInfo Target) { throw new System.Exception("Unable to find Python libs, please add a search path to linuxKnownLibsPaths"); } + PublicIncludePaths.Add(Path.Combine(ModuleDirectory,"Include")); PublicIncludePaths.Add(includesPath); PublicAdditionalLibraries.Add(libsPath); From 2ccfe75812db6c2d2a4b3a2b97688cb6215ae08d Mon Sep 17 00:00:00 2001 From: David Date: Tue, 30 Jun 2020 07:38:44 +0100 Subject: [PATCH 21/31] Changed to original order by default for FRotator arguments so doesnt break existing scripts. --- Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp index 6e50318a0..61646bd29 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp @@ -2,7 +2,7 @@ // use this definition if want original argument order for FRotator -//#define USE_UNREALENGINEPYTHON_ORDER 1 +#define USE_UNREALENGINEPYTHON_ORDER 1 static PyObject *py_ue_frotator_get_vector(ue_PyFRotator *self, PyObject * args) { From 7a21aceb70cc90749f76ee77415e55fd0f053108 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 4 Jul 2020 16:40:50 +0100 Subject: [PATCH 22/31] Added debug prints for property conversion from or to python. --- .../UnrealEnginePython/Private/UEPyModule.cpp | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index d3afb8ecf..e206fd354 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1365,6 +1365,7 @@ static int ue_PyUObject_setattro(ue_PyUObject* self, PyObject* attr_name, PyObje FProperty* f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); if (f_property) { + EXTRA_UE_LOG(LogPython, Warning, TEXT("Setting attr found %s"), UTF8_TO_TCHAR(attr)); #ifdef EXTRA_DEBUG_CODE if (!strcmp(attr, "TargetType")) { @@ -2512,6 +2513,7 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) if (auto casted_prop = CastField(prop)) { bool value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: bool %d"), value); if (value) { Py_RETURN_TRUE; @@ -2521,19 +2523,26 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) if (auto casted_prop = CastField(prop)) { + // well I thought there was an issue here but turns out this is fine - its the test_blueprint.py thats wrong + //void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: number int %p (%p) %x"), buffer, prop_addr, *((uint64*)prop_addr)); + //int value = casted_prop->GetPropertyValue(prop_addr); int value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: int %d %x"), value, value); return PyLong_FromLong(value); } if (auto casted_prop = CastField(prop)) { uint32 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: uint %d"), value); return PyLong_FromUnsignedLong(value); } if (auto casted_prop = CastField(prop)) { long long value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: ull %lld"), value); return PyLong_FromLongLong(value); } @@ -2541,18 +2550,21 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) if (auto casted_prop = CastField(prop)) { uint64 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: uint64 %lld"), value); return PyLong_FromUnsignedLongLong(value); } if (auto casted_prop = CastField(prop)) { float value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: float %f"), value); return PyFloat_FromDouble(value); } if (auto casted_prop = CastField(prop)) { uint8 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: uint8 %d"), value); return PyLong_FromUnsignedLong(value); } @@ -2560,6 +2572,7 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) { void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); uint64 enum_index = casted_prop->GetUnderlyingProperty()->GetUnsignedIntPropertyValue(prop_addr); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: enum %lld"), enum_index); return PyLong_FromUnsignedLong(enum_index); } @@ -2740,9 +2753,11 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) // convert a python object to a property bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, int32 index) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject")); if (PyBool_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: bool")); auto casted_prop = CastField(prop); if (!casted_prop) return false; @@ -2759,16 +2774,25 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in if (PyNumber_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: number")); if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); + //void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: number int %p (%p) %ld"), buffer, prop_addr, PyLong_AsLong(py_long)); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: number int %p (%p) %x"), buffer, prop_addr, *((uint64*)prop_addr)); + //casted_prop->SetIntPropertyValue(prop_addr, (int64) PyLong_AsLong(py_long)); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLong(py_long), index); Py_DECREF(py_long); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: number int %p (%p) %x"), buffer, prop_addr, *((uint64*)prop_addr)); return true; } if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); + //void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: number uint %p (%p) %ld"), buffer, prop_addr, PyLong_AsUnsignedLong(py_long)); + //casted_prop->SetIntPropertyValue(prop_addr, (uint64) PyLong_AsUnsignedLong(py_long)); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); Py_DECREF(py_long); return true; @@ -2777,6 +2801,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLongLong(py_long), index); + //casted_prop->SetIntPropertyValue(buffer, PyLong_AsLongLong(py_long), index); Py_DECREF(py_long); return true; } @@ -2784,6 +2809,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLongLong(py_long), index); + //casted_prop->SetIntPropertyValue(buffer, PyLong_AsUnsignedLongLong(py_long), index); Py_DECREF(py_long); return true; } @@ -2791,6 +2817,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in { PyObject* py_float = PyNumber_Float(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyFloat_AsDouble(py_float), index); + //casted_prop->SetFloatingPointPropertyValue(buffer, PyFloat_AsDouble(py_float), index); Py_DECREF(py_float); return true; } @@ -2810,11 +2837,14 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: number FAILED")); + return false; } if (PyUnicodeOrString_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: string")); if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj)), index); @@ -2830,11 +2860,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in casted_prop->SetPropertyValue_InContainer(buffer, FText::FromString(UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj))), index); return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: string FAILED")); return false; } if (PyBytes_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: bytes")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2862,11 +2894,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: bytes FAILED")); return false; } if (PyByteArray_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: array")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2895,11 +2929,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: bytearray FAILED")); return false; } if (PyList_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: list")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2928,11 +2964,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: list FAILED")); return false; } if (PyTuple_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: tuple")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2961,11 +2999,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: tuple FAILED")); return false; } if (PyDict_Check(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: dict")); if (auto casted_prop = CastField(prop)) { FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); @@ -2996,6 +3036,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: dict FAILED")); return false; } @@ -3003,6 +3044,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in if (ue_PyFVector * py_vec = py_ue_is_fvector(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fvector")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3011,11 +3053,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fvector FAILED")); return false; } if (ue_PyFVector2D * py_vec = py_ue_is_fvector2d(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fvector2D")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3024,11 +3068,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fvector2D FAILED")); return false; } if (ue_PyFRotator * py_rot = py_ue_is_frotator(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: frotator")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3037,11 +3083,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: frotator FAILED")); return false; } if (ue_PyFTransform * py_transform = py_ue_is_ftransform(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: ftransform")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3050,11 +3098,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: ftransform FAILED")); return false; } if (ue_PyFColor * py_color = py_ue_is_fcolor(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fcolor")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3064,11 +3114,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fcolor FAILED")); return false; } if (ue_PyFLinearColor * py_color = py_ue_is_flinearcolor(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: flinearcolor")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3077,11 +3129,13 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: flinearcolor FAILED")); return false; } if (ue_PyFHitResult * py_hit = py_ue_is_fhitresult(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fhitresult")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == FHitResult::StaticStruct()) @@ -3090,12 +3144,14 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fhitresult FAILED")); return false; } // generic structs if (py_ue_is_uscriptstruct(py_obj)) { + //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: uscriptstruct")); ue_PyUScriptStruct* py_u_struct = (ue_PyUScriptStruct*)py_obj; if (auto casted_prop = CastField(prop)) { @@ -3107,6 +3163,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: uscriptstruct FAILED")); return false; } @@ -3160,6 +3217,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: uclass FAILED")); return false; } @@ -3206,6 +3264,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: uobject FAILED")); return false; } @@ -3227,6 +3286,7 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } + EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: Py_None FAILED")); return false; } From 482355cb248fe9f0c75b2bd8fe362171b1eca19f Mon Sep 17 00:00:00 2001 From: David Date: Sat, 4 Jul 2020 16:41:50 +0100 Subject: [PATCH 23/31] Revert "Added debug prints for property conversion from or to python." This reverts commit 7a21aceb70cc90749f76ee77415e55fd0f053108. In case wish to re-add these debug prints at some point. --- .../UnrealEnginePython/Private/UEPyModule.cpp | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index e206fd354..d3afb8ecf 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -1365,7 +1365,6 @@ static int ue_PyUObject_setattro(ue_PyUObject* self, PyObject* attr_name, PyObje FProperty* f_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); if (f_property) { - EXTRA_UE_LOG(LogPython, Warning, TEXT("Setting attr found %s"), UTF8_TO_TCHAR(attr)); #ifdef EXTRA_DEBUG_CODE if (!strcmp(attr, "TargetType")) { @@ -2513,7 +2512,6 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) if (auto casted_prop = CastField(prop)) { bool value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: bool %d"), value); if (value) { Py_RETURN_TRUE; @@ -2523,26 +2521,19 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) if (auto casted_prop = CastField(prop)) { - // well I thought there was an issue here but turns out this is fine - its the test_blueprint.py thats wrong - //void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: number int %p (%p) %x"), buffer, prop_addr, *((uint64*)prop_addr)); - //int value = casted_prop->GetPropertyValue(prop_addr); int value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: int %d %x"), value, value); return PyLong_FromLong(value); } if (auto casted_prop = CastField(prop)) { uint32 value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: uint %d"), value); return PyLong_FromUnsignedLong(value); } if (auto casted_prop = CastField(prop)) { long long value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: ull %lld"), value); return PyLong_FromLongLong(value); } @@ -2550,21 +2541,18 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) if (auto casted_prop = CastField(prop)) { uint64 value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: uint64 %lld"), value); return PyLong_FromUnsignedLongLong(value); } if (auto casted_prop = CastField(prop)) { float value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: float %f"), value); return PyFloat_FromDouble(value); } if (auto casted_prop = CastField(prop)) { uint8 value = casted_prop->GetPropertyValue_InContainer(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: uint8 %d"), value); return PyLong_FromUnsignedLong(value); } @@ -2572,7 +2560,6 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) { void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); uint64 enum_index = casted_prop->GetUnderlyingProperty()->GetUnsignedIntPropertyValue(prop_addr); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: enum %lld"), enum_index); return PyLong_FromUnsignedLong(enum_index); } @@ -2753,11 +2740,9 @@ PyObject* ue_py_convert_property(FProperty* prop, uint8* buffer, int32 index) // convert a python object to a property bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, int32 index) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject")); if (PyBool_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: bool")); auto casted_prop = CastField(prop); if (!casted_prop) return false; @@ -2774,25 +2759,16 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in if (PyNumber_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: number")); if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); - //void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: number int %p (%p) %ld"), buffer, prop_addr, PyLong_AsLong(py_long)); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: number int %p (%p) %x"), buffer, prop_addr, *((uint64*)prop_addr)); - //casted_prop->SetIntPropertyValue(prop_addr, (int64) PyLong_AsLong(py_long)); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLong(py_long), index); Py_DECREF(py_long); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_property: number int %p (%p) %x"), buffer, prop_addr, *((uint64*)prop_addr)); return true; } if (auto casted_prop = CastField(prop)) { PyObject* py_long = PyNumber_Long(py_obj); - //void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: number uint %p (%p) %ld"), buffer, prop_addr, PyLong_AsUnsignedLong(py_long)); - //casted_prop->SetIntPropertyValue(prop_addr, (uint64) PyLong_AsUnsignedLong(py_long)); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); Py_DECREF(py_long); return true; @@ -2801,7 +2777,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsLongLong(py_long), index); - //casted_prop->SetIntPropertyValue(buffer, PyLong_AsLongLong(py_long), index); Py_DECREF(py_long); return true; } @@ -2809,7 +2784,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in { PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLongLong(py_long), index); - //casted_prop->SetIntPropertyValue(buffer, PyLong_AsUnsignedLongLong(py_long), index); Py_DECREF(py_long); return true; } @@ -2817,7 +2791,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in { PyObject* py_float = PyNumber_Float(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyFloat_AsDouble(py_float), index); - //casted_prop->SetFloatingPointPropertyValue(buffer, PyFloat_AsDouble(py_float), index); Py_DECREF(py_float); return true; } @@ -2837,14 +2810,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: number FAILED")); - return false; } if (PyUnicodeOrString_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: string")); if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj)), index); @@ -2860,13 +2830,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in casted_prop->SetPropertyValue_InContainer(buffer, FText::FromString(UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_obj))), index); return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: string FAILED")); return false; } if (PyBytes_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: bytes")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2894,13 +2862,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: bytes FAILED")); return false; } if (PyByteArray_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: array")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2929,13 +2895,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: bytearray FAILED")); return false; } if (PyList_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: list")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2964,13 +2928,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: list FAILED")); return false; } if (PyTuple_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: tuple")); if (auto casted_prop = CastField(prop)) { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); @@ -2999,13 +2961,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: tuple FAILED")); return false; } if (PyDict_Check(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: dict")); if (auto casted_prop = CastField(prop)) { FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); @@ -3036,7 +2996,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: dict FAILED")); return false; } @@ -3044,7 +3003,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in if (ue_PyFVector * py_vec = py_ue_is_fvector(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fvector")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3053,13 +3011,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fvector FAILED")); return false; } if (ue_PyFVector2D * py_vec = py_ue_is_fvector2d(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fvector2D")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3068,13 +3024,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fvector2D FAILED")); return false; } if (ue_PyFRotator * py_rot = py_ue_is_frotator(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: frotator")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3083,13 +3037,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: frotator FAILED")); return false; } if (ue_PyFTransform * py_transform = py_ue_is_ftransform(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: ftransform")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3098,13 +3050,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: ftransform FAILED")); return false; } if (ue_PyFColor * py_color = py_ue_is_fcolor(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fcolor")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3114,13 +3064,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fcolor FAILED")); return false; } if (ue_PyFLinearColor * py_color = py_ue_is_flinearcolor(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: flinearcolor")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == TBaseStructure::Get()) @@ -3129,13 +3077,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: flinearcolor FAILED")); return false; } if (ue_PyFHitResult * py_hit = py_ue_is_fhitresult(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: fhitresult")); if (auto casted_prop = CastField(prop)) { if (casted_prop->Struct == FHitResult::StaticStruct()) @@ -3144,14 +3090,12 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: fhitresult FAILED")); return false; } // generic structs if (py_ue_is_uscriptstruct(py_obj)) { - //EXTRA_UE_LOG(LogPython, Warning, TEXT("ue_py_convert_pyobject: uscriptstruct")); ue_PyUScriptStruct* py_u_struct = (ue_PyUScriptStruct*)py_obj; if (auto casted_prop = CastField(prop)) { @@ -3163,7 +3107,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: uscriptstruct FAILED")); return false; } @@ -3217,7 +3160,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: uclass FAILED")); return false; } @@ -3264,7 +3206,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: uobject FAILED")); return false; } @@ -3286,7 +3227,6 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in return true; } - EXTRA_UE_LOG(LogPython, Error, TEXT("ue_py_convert_pyobject: Py_None FAILED")); return false; } From c0a0089054c7e67f613d776e487e2beffc35d029 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 5 Jul 2020 13:33:52 +0100 Subject: [PATCH 24/31] More updates to fix 4.25 deprecations. --- .../Private/PythonProjectEditorCommands.cpp | 14 ++++++++++++++ .../UnrealEnginePython/Private/Slate/UEPySlate.cpp | 7 +++++++ .../Private/UObject/UEPyCapture.cpp | 5 +++++ .../Private/UObject/UEPyPlayer.cpp | 4 ++++ 4 files changed, 30 insertions(+) diff --git a/Source/PythonEditor/Private/PythonProjectEditorCommands.cpp b/Source/PythonEditor/Private/PythonProjectEditorCommands.cpp index 598049d73..76161f8a2 100644 --- a/Source/PythonEditor/Private/PythonProjectEditorCommands.cpp +++ b/Source/PythonEditor/Private/PythonProjectEditorCommands.cpp @@ -14,6 +14,19 @@ FPythonProjectEditorCommands::FPythonProjectEditorCommands() void FPythonProjectEditorCommands::RegisterCommands() { +#if ENGINE_MINOR_VERSION >= 25 + UI_COMMAND(New, "New", "New Python Script.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::N)); + UI_COMMAND(NewDirectory, "New Directory", "New Directory.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::N)); + UI_COMMAND(Delete, "Delete", "Delete Python Script.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::D)); + + UI_COMMAND(Save, "Save", "Save the currently active document.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::S)); + UI_COMMAND(SaveAll, "Save All", "Save all open documents.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::S)); + UI_COMMAND(Execute, "Execute", "Execute Current Python File.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::Enter)); +#if PLATFORM_MAC + UI_COMMAND(ExecuteInMainThread, "Execute In Main Thread", "Execute Current Python File in the Main Thread.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::Enter)); +#endif + UI_COMMAND(PEP8ize, "PEP8-ize", "Enforce PEP8 to the current code.", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::P)); +#else UI_COMMAND(New, "New", "New Python Script.", EUserInterfaceActionType::Button, FInputGesture(EModifierKey::Control, EKeys::N)); UI_COMMAND(NewDirectory, "New Directory", "New Directory.", EUserInterfaceActionType::Button, FInputGesture(EModifierKey::Control | EModifierKey::Shift, EKeys::N)); UI_COMMAND(Delete, "Delete", "Delete Python Script.", EUserInterfaceActionType::Button, FInputGesture(EModifierKey::Control, EKeys::D)); @@ -25,6 +38,7 @@ void FPythonProjectEditorCommands::RegisterCommands() UI_COMMAND(ExecuteInMainThread, "Execute In Main Thread", "Execute Current Python File in the Main Thread.", EUserInterfaceActionType::Button, FInputGesture(EModifierKey::Control | EModifierKey::Shift, EKeys::Enter)); #endif UI_COMMAND(PEP8ize, "PEP8-ize", "Enforce PEP8 to the current code.", EUserInterfaceActionType::Button, FInputGesture(EModifierKey::Control | EModifierKey::Shift, EKeys::P)); +#endif } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index 2e705a303..85d2b8f4f 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -1020,10 +1020,17 @@ class FPythonSlateCommands : public TCommands virtual void RegisterCommands() override { commands = MakeShareable(new FUICommandList); +#if ENGINE_MINOR_VERSION >= 25 + // probabaly not the way to do this Runtime/Slate/Public/Framework/Commands/Commands.h says to use FUICommandInfo::MakeCommandInfo + // - apparently only the macros are used for localization - ie this will not get localized + // not clear why the UI_COMMAND macro is not used here - which simply expands to the following call + MakeUICommand_InternalUseOnly(this, command, nullptr, *name, *name, TCHAR_TO_UTF8(*name), *name, *name, EUserInterfaceActionType::Button, FInputChord()); +#else #if ENGINE_MINOR_VERSION >= 23 MakeUICommand_InternalUseOnly(this, command, nullptr, *name, *name, TCHAR_TO_UTF8(*name), *name, *name, EUserInterfaceActionType::Button, FInputGesture()); #else UI_COMMAND_Function(this, command, nullptr, *name, *name, TCHAR_TO_UTF8(*name), *name, *name, EUserInterfaceActionType::Button, FInputGesture()); +#endif #endif commands->MapAction(command, FExecuteAction::CreateRaw(this, &FPythonSlateCommands::Callback), FCanExecuteAction()); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 55bc24a0e..3f5255623 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -212,7 +212,12 @@ struct FInEditorMultiCapture : TSharedFromThis PlayInEditorSettings->LaunchConfiguration = EPlayOnLaunchConfiguration::LaunchConfig_Default; PlayInEditorSettings->SetPlayNetMode(EPlayNetMode::PIE_Standalone); PlayInEditorSettings->SetRunUnderOneProcess(true); +#if ENGINE_MINOR_VERSION >= 25 + // I hope this is equivalent ie play dedicated is same as launching a separate server + PlayInEditorSettings->bLaunchSeparateServer = false; +#else PlayInEditorSettings->SetPlayNetDedicated(false); +#endif PlayInEditorSettings->SetPlayNumberOfClients(1); } diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp index 1a7be4f29..41c36c649 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyPlayer.cpp @@ -155,7 +155,11 @@ PyObject *py_ue_create_player(ue_PyUObject *self, PyObject * args) if (!controller) return PyErr_Format(PyExc_Exception, "unable to create a new player from controller %d", controller_id); +#if ENGINE_MINOR_VERSION >= 25 + return PyLong_FromLong(controller->PlayerState->GetPlayerId()); +#else return PyLong_FromLong(controller->PlayerState->PlayerId); +#endif } PyObject *py_ue_get_num_players(ue_PyUObject *self, PyObject * args) From 584f78f949f09b0b5cf9a3c1785c10931c6c00f6 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 5 Jul 2020 13:41:29 +0100 Subject: [PATCH 25/31] Possible fixup for PlayInEditor 4.25 changes. This involves some code moving which will be executed at different times. Builds but not really tested. --- .../Private/UObject/UEPyCapture.cpp | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 3f5255623..fb41b6ab4 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp @@ -105,12 +105,21 @@ struct FInEditorMultiCapture : TSharedFromThis } } + // should I move this into PlaySession also?? + // cleanup from previous run BackedUpPlaySettings.Empty(); FObjectWriter(PlayInEditorSettings, BackedUpPlaySettings); +#if ENGINE_MINOR_VERSION >= 25 + // can I move this from here to PlaySession?? + // otherwise need to store the settings somehow + // currently assuming no changes to default PlayInEditorSettings between the Dequeue and PlaySession + //OverridePlaySettings(PlayInEditorSettings); +#else OverridePlaySettings(PlayInEditorSettings); +#endif //CurrentCaptureObject->AddToRoot(); CurrentCaptureObject->OnCaptureFinished().AddRaw(this, &FInEditorMultiCapture::OnEnd); @@ -156,10 +165,45 @@ struct FInEditorMultiCapture : TSharedFromThis #if ENGINE_MINOR_VERSION >= 25 + // we also need access to this + const FMovieSceneCaptureSettings& Settings = CurrentCaptureObject->GetSettings(); + + // as commented this is from Editor/MovieSceneCaptureDialog/Private/MovieSceneCaptureDialogModule.cpp + // and this is where that source code sets the custom window now + TSharedRef CustomWindow = SNew(SWindow) + .Title(FText::FromString("Movie Render - Preview")) + .AutoCenter(EAutoCenter::PrimaryWorkArea) + .UseOSWindowBorder(true) + .FocusWhenFirstShown(false) + .ActivationPolicy(EWindowActivationPolicy::Never) + .HasCloseButton(true) + .SupportsMaximize(false) + .SupportsMinimize(true) + .MaxWidth(Settings.Resolution.ResX) + .MaxHeight(Settings.Resolution.ResY) + .SizingRule(ESizingRule::FixedSize); + + FSlateApplication::Get().AddWindow(CustomWindow); + + // is there any reason NOT to call this here?? + ULevelEditorPlaySettings* PlayInEditorSettings = GetMutableDefault(); + OverridePlaySettings(PlayInEditorSettings); + + // this is supposed to be how it works but cant figure out where is bAtPlayerStart // well basically because this is just ignored!! + // note that 3rd param is bInSimulateInEditor - which only sets play_params.WorldType if true + // (see code in Editor/UnrealEd/Private/PlayLevel.cpp) FRequestPlaySessionParams play_params; play_params.DestinationSlateViewport = nullptr; + //play_params.WorldType = EPlaySessionWorldType::SimulateInEditor; + + // NO - we have a big problem - this is in same function in MovieSceneCaptureDialogModule.cpp + // - not clear how we get this here - unless we call OverridePlaySettings in this function as above + play_params.EditorPlaySettings = PlayInEditorSettings; + + play_params.CustomPIEWindow = CustomWindow; + GEditor->RequestPlaySession(play_params); #else GEditor->RequestPlaySession(true, nullptr, false); @@ -176,6 +220,10 @@ struct FInEditorMultiCapture : TSharedFromThis PlayInEditorSettings->CenterNewWindow = true; PlayInEditorSettings->LastExecutedPlayModeType = EPlayModeType::PlayMode_InEditorFloating; +#if ENGINE_MINOR_VERSION >= 25 + // this is complicated - instead of specifying here we need to specify in FRequestPlaySessionParams + // but that means this needs to be done in the PlaySession function +#else TSharedRef CustomWindow = SNew(SWindow) .Title(FText::FromString("Movie Render - Preview")) .AutoCenter(EAutoCenter::PrimaryWorkArea) @@ -194,6 +242,7 @@ struct FInEditorMultiCapture : TSharedFromThis FSlateApplication::Get().AddWindow(CustomWindow); PlayInEditorSettings->CustomPIEWindow = CustomWindow; +#endif // Reset everything else PlayInEditorSettings->GameGetsMouseControl = false; From 1ce237eaf6ade198b2d933431771bea0d2c825be Mon Sep 17 00:00:00 2001 From: jonlm <19480642+jonlm@users.noreply.github.com> Date: Tue, 30 Apr 2019 17:44:39 -0700 Subject: [PATCH 26/31] Fix the blueprint variable unit test. --- tests/test_blueprint.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_blueprint.py b/tests/test_blueprint.py index 5f755848f..5a8e8f020 100644 --- a/tests/test_blueprint.py +++ b/tests/test_blueprint.py @@ -29,6 +29,7 @@ def test_spawn(self): def test_variable(self): new_blueprint = ue.create_blueprint(Character, '/Game/Tests/Blueprints/Test2_' + self.random_string) ue.blueprint_add_member_variable(new_blueprint, 'TestValue', 'int') + ue.blueprint_set_variable_visibility(new_blueprint, 'TestValue', 1) ue.compile_blueprint(new_blueprint) new_actor = self.world.actor_spawn(new_blueprint.GeneratedClass) new_actor.TestValue = 17 From c686eb1a614bb013213ab5cca3d5c7ab2ba84796 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 5 Jul 2020 14:18:24 +0100 Subject: [PATCH 27/31] Added new versions of some examples to work in 4.25. May NOT be a complete set of changes required as just tested these examples. --- examples/blueprint_dynamic_cast_425.py | 27 +++++++++++++ examples/custom_settings_425.py | 44 +++++++++++++++++++++ examples/graphs_creator_425.py | 55 ++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 examples/blueprint_dynamic_cast_425.py create mode 100644 examples/custom_settings_425.py create mode 100644 examples/graphs_creator_425.py diff --git a/examples/blueprint_dynamic_cast_425.py b/examples/blueprint_dynamic_cast_425.py new file mode 100644 index 000000000..9c0e55ad9 --- /dev/null +++ b/examples/blueprint_dynamic_cast_425.py @@ -0,0 +1,27 @@ +import unreal_engine as ue +from unreal_engine.classes import Blueprint, K2Node_DynamicCast, Actor, Object +from unreal_engine.structs import EdGraphPinType +from unreal_engine.enums import EEdGraphPinDirection + +bp_foo = ue.load_object(Blueprint, '/Game/Foo.Foo') +bp_bar = ue.load_object(Blueprint, '/Game/Bar.Bar') + +graph = ue.blueprint_add_function(bp_foo, 'FooCaster') +func = graph.Nodes[0] + +cast_node = K2Node_DynamicCast(Outer=graph) +cast_node.TargetType = bp_bar.GeneratedClass + +pin_type = EdGraphPinType(PinCategory = 'object', PinSubCategoryObject=Actor) +pin = func.node_create_pin(EEdGraphPinDirection.EGPD_Input, pin_type, 'Arg001') + + +graph.graph_add_node(cast_node, 600, 0) + +cast_node.node_find_pin('Object').category = 'object' +cast_node.node_find_pin('Object').sub_category = Object + +pin.make_link_to(cast_node.node_find_pin('Object')) +func.node_find_pin('then').make_link_to(cast_node.node_find_pin('execute')) + +ue.compile_blueprint(bp_foo) diff --git a/examples/custom_settings_425.py b/examples/custom_settings_425.py new file mode 100644 index 000000000..ff1ddb117 --- /dev/null +++ b/examples/custom_settings_425.py @@ -0,0 +1,44 @@ +import unreal_engine as ue + +from unreal_engine.classes import Object +#from unreal_engine.classes import StrProperty, IntProperty +from unreal_engine.properties import StrProperty, IntProperty +from unreal_engine import CLASS_CONFIG, CLASS_DEFAULT_CONFIG, CPF_CONFIG + +def config(arg): + config_name = None + def wrapper(u_class): + cflags = u_class.class_get_flags() + u_class.class_set_flags(cflags|CLASS_CONFIG) + if config_name: + u_class.class_set_config_name(config_name) + return u_class + if isinstance(arg, str): + config_name = arg + return wrapper + return wrapper(arg) + +def default_config(u_class): + cflags = u_class.class_get_flags() + u_class.class_set_flags(cflags|CLASS_DEFAULT_CONFIG) + return u_class + +@config('DumbConfig') +@default_config +class FooSettings(Object): + + HelloWorld = StrProperty + + FooWorld = [IntProperty] + + def __init__(self): + self.add_property_flags('HelloWorld', CPF_CONFIG) + self.get_fproperty('HelloWorld').set_metadata('Category', 'CategoryTest001') + self.HelloWorld = 'Hello World 001' + + self.add_property_flags('FooWorld', CPF_CONFIG) + self.get_fproperty('FooWorld').set_metadata('Category', 'CategoryTest002') + self.FooWorld = [17, 22, 30] + + +ue.register_settings('Project', 'FooBar', 'General', 'General DisplayName', 'General Description', ue.get_mutable_default(FooSettings)) diff --git a/examples/graphs_creator_425.py b/examples/graphs_creator_425.py new file mode 100644 index 000000000..6c4a11fdd --- /dev/null +++ b/examples/graphs_creator_425.py @@ -0,0 +1,55 @@ +import unreal_engine as ue +from unreal_engine.classes import K2Node_InputKey, K2Node_SpawnActorFromClass, Actor, Character, KismetMathLibrary +from unreal_engine.structs import Key + +# create a new blueprint +new_blueprint = ue.create_blueprint(Actor, '/Game/StrangeBlueprint') + +# add a member float variable +ue.blueprint_add_member_variable(new_blueprint, 'Speed', 'float') + +# get a reference to the first graph page +uber_page = new_blueprint.UberGraphPages[0] + +# get good coordinates for a new node +x, y = uber_page.graph_get_good_place_for_new_node() +# add the Speed variable to the graph +uber_page.graph_add_node_variable_get('Speed', None, x, y) + +x, y = uber_page.graph_get_good_place_for_new_node() +# add a custom event to the graph +hello_world = uber_page.graph_add_node_custom_event('Hello World', x, y) + +# get a reference to the 'then' pin +hello_world_then = hello_world.node_find_pin('then') + +x, y = uber_page.graph_get_good_place_for_new_node() +# add a 'Spawn Actor From Class' node +spawn_actor_node = uber_page.graph_add_node(K2Node_SpawnActorFromClass, x, y) + +# set its Class pin +pin_class = spawn_actor_node.node_find_pin('Class') +pin_class.default_object = Character + +# get a reference to its 'exec' pin +spawn_actor_node_exec = spawn_actor_node.node_find_pin('execute') + +# link the hello world event to the spawn actor node +hello_world_then.make_link_to(spawn_actor_node_exec) + +x, y = uber_page.graph_get_good_place_for_new_node() +# create a 'make transform' node +make_transform = uber_page.graph_add_node_call_function(KismetMathLibrary.MakeTransform, x, y) + +# link the return value of 'make transform' to the 'SpawnTransform' pin of the spawn actor node +make_transform.node_find_pin('ReturnValue').make_link_to(spawn_actor_node.node_find_pin('SpawnTransform')) + +input_key = K2Node_InputKey(Outer=uber_page) +input_key.InputKey = Key(KeyName='SpaceBar') +input_key_node = uber_page.graph_add_node(input_key, 400, 400) + +# compile the blueprint +ue.compile_blueprint(new_blueprint) + +# save it +ue.editor_save_all() From 9da24f43f5114ce95f2d340deaf488078516a4af Mon Sep 17 00:00:00 2001 From: David Date: Fri, 17 Jul 2020 16:49:48 +0100 Subject: [PATCH 28/31] Correct debug code - added EXTRA_DEBUG_CODE ifdef wrap. --- Source/UnrealEnginePython/Private/UEPyModule.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index d3afb8ecf..2a42a68a6 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -3122,9 +3122,11 @@ bool ue_py_convert_pyobject(PyObject* py_obj, FProperty* prop, uint8* buffer, in if (auto casted_prop = CastField(prop)) { casted_prop->SetPropertyValue_InContainer(buffer, ue_obj->ue_object, index); +#ifdef EXTRA_DEBUG_CODE EXTRA_UE_LOG(LogPython, Warning, TEXT("Convert Prop 3a is uclass %s"), *ue_obj->ue_object->GetName()); UK2Node_DynamicCast* node = (UK2Node_DynamicCast*)buffer; EXTRA_UE_LOG(LogPython, Warning, TEXT("Setting attr targetype is %p"), (void *)(node->TargetType)); +#endif return true; } else if (auto casted_prop_soft_class = CastField(prop)) From 06ef4b33dd471dd69adee170ca233c2e87e5456a Mon Sep 17 00:00:00 2001 From: David Date: Fri, 17 Jul 2020 16:50:47 +0100 Subject: [PATCH 29/31] Added pragmas to convert -Wshadow from errors to warnings for Fbx module in order to compile. Note the actual errors are in Unreals fbx include files. --- Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.h | 1 + Source/UnrealEnginePython/Private/Fbx/UEPyFbxManager.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.h b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.h index 57ed9d573..8c7b24772 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.h +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxImporter.h @@ -6,6 +6,7 @@ #if PLATFORM_LINUX #if defined(__clang__) #pragma clang diagnostic ignored "-Wnull-dereference" +#pragma clang diagnostic warning "-Wshadow" #endif #endif diff --git a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxManager.h b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxManager.h index 59695148d..60c8d3f93 100644 --- a/Source/UnrealEnginePython/Private/Fbx/UEPyFbxManager.h +++ b/Source/UnrealEnginePython/Private/Fbx/UEPyFbxManager.h @@ -6,6 +6,7 @@ #if PLATFORM_LINUX #if defined(__clang__) #pragma clang diagnostic ignored "-Wnull-dereference" +#pragma clang diagnostic warning "-Wshadow" #endif #endif From d04c3af01547208ad3fc70409c509c345c5a1be9 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 20 Jul 2020 13:52:41 +0100 Subject: [PATCH 30/31] Wrapped more editor only functions with WITH_EDITOR to allow package building. --- Source/UnrealEnginePython/Private/UEPyModule.cpp | 7 +++++++ Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp | 3 +++ Source/UnrealEnginePython/Private/UObject/UEPyMaterial.h | 2 ++ Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp | 2 ++ Source/UnrealEnginePython/Private/UObject/UEPyObject.h | 2 ++ Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp | 3 +++ Source/UnrealEnginePython/Private/UObject/UEPyProperty.h | 3 +++ 7 files changed, 22 insertions(+) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index 2a42a68a6..2a882053b 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -619,8 +619,10 @@ static PyMethodDef ue_PyUObject_methods[] = { #endif #if ENGINE_MINOR_VERSION >= 15 +#if WITH_EDITOR { "can_modify", (PyCFunction)py_ue_can_modify, METH_VARARGS, "" }, #endif +#endif { "set_name", (PyCFunction)py_ue_set_name, METH_VARARGS, "" }, @@ -1156,7 +1158,9 @@ static PyMethodDef ue_PyUObject_methods[] = { { "get_material_scalar_parameter", (PyCFunction)py_ue_get_material_scalar_parameter, METH_VARARGS, "" }, { "get_material_vector_parameter", (PyCFunction)py_ue_get_material_vector_parameter, METH_VARARGS, "" }, { "get_material_texture_parameter", (PyCFunction)py_ue_get_material_texture_parameter, METH_VARARGS, "" }, +#if WITH_EDITOR { "get_material_static_switch_parameter", (PyCFunction)py_ue_get_material_static_switch_parameter, METH_VARARGS, "" }, +#endif { "create_material_instance_dynamic", (PyCFunction)py_ue_create_material_instance_dynamic, METH_VARARGS, "" }, #if WITH_EDITOR { "set_material_parent", (PyCFunction)py_ue_set_material_parent, METH_VARARGS, "" }, @@ -1705,9 +1709,12 @@ static int ue_PyFProperty_setattro(ue_PyFProperty* self, PyObject* attr_name, Py static PyMethodDef ue_PyFProperty_methods[] = { +#if WITH_EDITOR +// strictly WITH_EDITORONLY_DATA { "get_metadata", (PyCFunction)py_ue_fproperty_get_metadata, METH_VARARGS, "" }, { "set_metadata", (PyCFunction)py_ue_fproperty_set_metadata, METH_VARARGS, "" }, { "has_metadata", (PyCFunction)py_ue_fproperty_has_metadata, METH_VARARGS, "" }, +#endif { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index 19d8653b6..b14f19435 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -221,6 +221,8 @@ PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *self, PyObject * arg } +#if WITH_EDITOR +// strictly this is WITH_EDITORONLY_DATA PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObject * args) { @@ -253,6 +255,7 @@ PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *self, PyObjec Py_RETURN_FALSE; } +#endif PyObject *py_ue_set_material_vector_parameter(ue_PyUObject *self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.h b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.h index 95ebe3942..f156cd72d 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.h @@ -12,7 +12,9 @@ PyObject *py_ue_set_material_texture_parameter(ue_PyUObject *, PyObject *); PyObject *py_ue_get_material_scalar_parameter(ue_PyUObject *, PyObject *); PyObject *py_ue_get_material_vector_parameter(ue_PyUObject *, PyObject *); PyObject *py_ue_get_material_texture_parameter(ue_PyUObject *, PyObject *); +#if WITH_EDITOR PyObject *py_ue_get_material_static_switch_parameter(ue_PyUObject *, PyObject *); +#endif PyObject *py_ue_create_material_instance_dynamic(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 76ceaafb6..355426a3c 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -672,6 +672,7 @@ PyObject *py_ue_find_function(ue_PyUObject * self, PyObject * args) } #if ENGINE_MINOR_VERSION >= 15 +#if WITH_EDITOR PyObject *py_ue_can_modify(ue_PyUObject *self, PyObject * args) { @@ -685,6 +686,7 @@ PyObject *py_ue_can_modify(ue_PyUObject *self, PyObject * args) Py_RETURN_FALSE; } #endif +#endif PyObject *py_ue_set_name(ue_PyUObject *self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index 5afe1a8ea..198d05745 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -61,7 +61,9 @@ PyObject *py_ue_add_property(ue_PyUObject *, PyObject *); PyObject *py_ue_as_dict(ue_PyUObject *, PyObject *); +#if WITH_EDITOR PyObject *py_ue_can_modify(ue_PyUObject *, PyObject *); +#endif PyObject *py_ue_get_outer(ue_PyUObject *, PyObject *); PyObject *py_ue_set_outer(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp index e0457d69e..ebd8d6dbc 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp @@ -134,6 +134,8 @@ FProperty* FProperty_New(FFieldVariant* scope, FFieldClass* f_class, FName name, return nullptr; } +#if WITH_EDITOR +// strictly WITH_EDITORONLY_DATA PyObject *py_ue_fproperty_set_metadata(ue_PyFProperty * self, PyObject * args) { @@ -212,5 +214,6 @@ PyObject *py_ue_fproperty_has_metadata(ue_PyFProperty * self, PyObject * args) Py_INCREF(Py_False); return Py_False; } +#endif #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h index 09c8f7a01..64a0e5177 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h @@ -7,8 +7,11 @@ #if ENGINE_MINOR_VERSION >= 25 PyObject *get_fproperty_uclass(FProperty *); FProperty* FProperty_New(FFieldVariant* scope, FFieldClass* f_class, FName name, EObjectFlags o_flags); +#if WITH_EDITOR +// strictly WITH_EDITORONLY_DATA PyObject *py_ue_fproperty_set_metadata(ue_PyFProperty * self, PyObject * args); PyObject *py_ue_fproperty_get_metadata(ue_PyFProperty * self, PyObject * args); PyObject *py_ue_fproperty_has_metadata(ue_PyFProperty * self, PyObject * args); #endif +#endif From 1a8cd3efc02374041eab58e13b8044b03efb3b8b Mon Sep 17 00:00:00 2001 From: David Date: Wed, 22 Jul 2020 13:34:24 +0100 Subject: [PATCH 31/31] Created new type check functions for FProperty and updated py_ue_get_inner, py_ue_get_key_prop and py_ue_get_value_prop to use them. --- .../UnrealEnginePython/Private/UEPyModule.h | 44 +++++++++++++++ .../Private/UObject/UEPyObject.cpp | 54 +++++++++++++------ .../Private/UObject/UEPyObject.h | 5 +- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index 8d9b997ab..42492fed9 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -78,6 +78,50 @@ template T *ue_py_check_type(ue_PyUObject *py_obj) return Cast(py_obj->ue_object); } +#if ENGINE_MINOR_VERSION >= 25 + +template T *ue_py_check_ftype(PyObject *py_obj) +{ + ue_PyFProperty *ue_py_prop = ue_is_pyfproperty(py_obj); + if (!ue_py_prop) + { + return nullptr; + } + + // I cannot see any function IsValidLowLevel or IsPendingKillOrUnreachable in the FProperty definition in UnrealType.h + //if (!ue_py_obj->ue_object || !ue_py_obj->ue_object->IsValidLowLevel() || ue_py_obj->ue_object->IsPendingKillOrUnreachable()) + //{ + // UE_LOG(LogPython, Error, TEXT("invalid UObject in ue_PyUObject %p"), ue_py_obj); + // return nullptr; + //} + + if (!ue_py_prop->ue_fproperty) + { + UE_LOG(LogPython, Error, TEXT("invalid FProperty in ue_PyFProperty %p"), ue_py_prop); + return nullptr; + } + + return CastField(ue_py_prop->ue_fproperty); +} + +template T *ue_py_check_ftype(ue_PyFProperty *py_prop) +{ + // I cannot see any function IsValidLowLevel or IsPendingKillOrUnreachable in the FProperty definition in UnrealType.h + //if (!py_obj->ue_object || !py_obj->ue_object->IsValidLowLevel() || py_obj->ue_object->IsPendingKillOrUnreachable()) + //{ + // UE_LOG(LogPython, Error, TEXT("invalid UObject in ue_PyUObject %p"), py_obj); + // return nullptr; + //} + if (!py_prop->ue_fproperty) + { + UE_LOG(LogPython, Error, TEXT("invalid FProperty in ue_PyFProperty %p"), py_prop); + return nullptr; + } + return CastField(py_prop->ue_fproperty); +} + +#endif + uint8 *do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct); template T *ue_py_check_struct(PyObject *py_obj) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 355426a3c..9929cb79a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -1457,13 +1457,15 @@ PyObject *py_ue_get_uproperty(ue_PyUObject *self, PyObject * args) } #endif -PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_inner(ue_PyFProperty *self, PyObject * args) { - ue_py_check(self); + // not clear if need this - this checks if the UObject is in some invalid state + // - Im not sure FProperty has such a state - although I have seen some mentions of PendingDelete possible + //ue_py_check(self); -#if ENGINE_MINOR_VERSION >= 25 - FArrayProperty *f_property = ue_py_check_type(self); + FArrayProperty *f_property = ue_py_check_ftype(self); if (!f_property) return PyErr_Format(PyExc_Exception, "object is not a FArrayProperty"); @@ -1472,7 +1474,13 @@ PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_FPROPERTY(inner); +} #else +PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + UArrayProperty *u_property = ue_py_check_type(self); if (!u_property) return PyErr_Format(PyExc_Exception, "object is not a UArrayProperty"); @@ -1482,16 +1490,18 @@ PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_UOBJECT(inner); -#endif } +#endif -PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_key_prop(ue_PyFProperty *self, PyObject * args) { - ue_py_check(self); + // not clear if need this - this checks if the UObject is in some invalid state + // - Im not sure FProperty has such a state - although I have seen some mentions of PendingDelete possible + //ue_py_check(self); -#if ENGINE_MINOR_VERSION >= 25 - FMapProperty *f_property = ue_py_check_type(self); + FMapProperty *f_property = ue_py_check_ftype(self); if (!f_property) return PyErr_Format(PyExc_Exception, "object is not a FMapProperty"); @@ -1500,7 +1510,13 @@ PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_FPROPERTY(key); +} #else +PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + UMapProperty *u_property = ue_py_check_type(self); if (!u_property) return PyErr_Format(PyExc_Exception, "object is not a UMapProperty"); @@ -1510,16 +1526,18 @@ PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_UOBJECT(key); -#endif } +#endif -PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_value_prop(ue_PyFProperty *self, PyObject * args) { - ue_py_check(self); + // not clear if need this - this checks if the UObject is in some invalid state + // - Im not sure FProperty has such a state - although I have seen some mentions of PendingDelete possible + //ue_py_check(self); -#if ENGINE_MINOR_VERSION >= 25 - FMapProperty *f_property = ue_py_check_type(self); + FMapProperty *f_property = ue_py_check_ftype(self); if (!f_property) return PyErr_Format(PyExc_Exception, "object is not a FMapProperty"); @@ -1528,7 +1546,13 @@ PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_FPROPERTY(value); +} #else +PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + UMapProperty *u_property = ue_py_check_type(self); if (!u_property) return PyErr_Format(PyExc_Exception, "object is not a UMapProperty"); @@ -1538,8 +1562,8 @@ PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; Py_RETURN_UOBJECT(value); -#endif } +#endif PyObject *py_ue_has_property(ue_PyUObject *self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index 198d05745..15f902f1d 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -26,12 +26,15 @@ PyObject *py_ue_get_property(ue_PyUObject *, PyObject *); PyObject *py_ue_get_property_array_dim(ue_PyUObject *, PyObject *); #if ENGINE_MINOR_VERSION >= 25 PyObject *py_ue_get_fproperty(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_inner(ue_PyFProperty *, PyObject *); +PyObject *py_ue_get_key_prop(ue_PyFProperty *, PyObject *); +PyObject *py_ue_get_value_prop(ue_PyFProperty *, PyObject *); #else PyObject *py_ue_get_uproperty(ue_PyUObject *, PyObject *); -#endif PyObject *py_ue_get_inner(ue_PyUObject *, PyObject *); PyObject *py_ue_get_key_prop(ue_PyUObject *, PyObject *); PyObject *py_ue_get_value_prop(ue_PyUObject *, PyObject *); +#endif PyObject *py_ue_get_property_class(ue_PyUObject *, PyObject *); PyObject *py_ue_has_property(ue_PyUObject *, PyObject *); PyObject *py_ue_is_rooted(ue_PyUObject *, PyObject *);