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. 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..de74a2dac 100644 --- a/Source/PythonEditor/Private/PythonEditor.cpp +++ b/Source/PythonEditor/Private/PythonEditor.cpp @@ -7,6 +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" ) ); @@ -90,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 33b2a56c8..d5b90c6c5 100644 --- a/Source/PythonEditor/Private/PythonProjectEditor.cpp +++ b/Source/PythonEditor/Private/PythonProjectEditor.cpp @@ -1,6 +1,13 @@ // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #include "PythonProjectEditor.h" + +#if ENGINE_MINOR_VERSION >= 24 +#include "Subsystems/AssetEditorSubsystem.h" + +// #include "UnrealEd.h" +#endif + #include "SPythonEditor.h" #include "SPythonProjectEditor.h" #include "Runtime/Slate/Public/Widgets/Docking/SDockTab.h" @@ -13,6 +20,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 +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/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/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/Blueprint/UEPyEdGraph.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp index a3e53b7c5..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); } @@ -654,6 +662,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/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 diff --git a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp index 0b80f1466..3239c2f07 100644 --- a/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp +++ b/Source/UnrealEnginePython/Private/MaterialEditorUtilities/UEPyFMaterialEditorUtilities.cpp @@ -1,7 +1,12 @@ #include "UEPyFMaterialEditorUtilities.h" + #if WITH_EDITOR +#if ENGINE_MINOR_VERSION >= 24 +#include "Subsystems/AssetEditorSubsystem.h" +#endif + #include "Materials/Material.h" #include "Runtime/Engine/Classes/EdGraph/EdGraph.h" @@ -54,7 +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"); @@ -128,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/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/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index 3e0b978ed..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 "Wrappers/UEPyESlateEnums.h" static PyObject* py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder* self, PyObject* args) @@ -115,6 +115,9 @@ 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 +143,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/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; } 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/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/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index 1b3df7bca..4e2a1147e 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(); @@ -88,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; @@ -110,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; @@ -277,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; @@ -1140,7 +1177,13 @@ 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) @@ -1168,7 +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; } @@ -1193,7 +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"); @@ -1212,14 +1266,24 @@ 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"); + +#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; } @@ -1288,7 +1352,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) @@ -1355,7 +1423,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); @@ -2015,7 +2083,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) @@ -2023,7 +2095,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(); @@ -2127,7 +2203,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..e724935c6 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())); } @@ -589,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 56954d32e..2a882053b 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 @@ -203,8 +214,11 @@ static PyObject* py_ue_get_py_proxy(ue_PyUObject* self, PyObject* args) static PyObject* py_unreal_engine_shutdown(PyObject* self, PyObject* args) { +#if ENGINE_MINOR_VERSION >= 24 + RequestEngineExit(FString(TEXT("I'm Shutting Down, Dave..."))); +#else GIsRequestingExit = true; - +#endif Py_RETURN_NONE; } @@ -569,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, "" }, @@ -601,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, "" }, @@ -675,6 +695,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, "" }, @@ -1136,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, "" }, @@ -1213,6 +1237,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()) @@ -1223,6 +1248,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) { @@ -1230,6 +1264,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 @@ -1315,10 +1350,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()) @@ -1329,6 +1365,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) { @@ -1366,6 +1457,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)))) @@ -1397,6 +1489,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"); @@ -1411,7 +1504,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); @@ -1421,6 +1529,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); @@ -1438,6 +1550,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 @@ -1473,6 +1586,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) { @@ -1487,6 +1616,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); @@ -1527,6 +1657,230 @@ 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[] = { +#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 */ +}; + +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 + @@ -1559,6 +1913,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) { @@ -1574,6 +1931,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; @@ -1636,6 +2001,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() { @@ -1644,6 +2013,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); @@ -1654,6 +2024,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; @@ -1750,6 +2146,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)); @@ -1896,49 +2295,148 @@ 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) + { + Py_INCREF(ret); + } + return ret; +} + + +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; } @@ -2014,10 +2512,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) @@ -2027,70 +2526,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) @@ -2100,7 +2598,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) @@ -2111,7 +2609,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)) { @@ -2155,7 +2653,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(); @@ -2167,24 +2665,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()); @@ -2207,8 +2705,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); @@ -2243,18 +2740,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)) @@ -2270,50 +2766,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); @@ -2321,25 +2816,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; @@ -2349,11 +2842,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); @@ -2381,11 +2874,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); @@ -2414,11 +2907,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 @@ -2447,11 +2940,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 @@ -2478,10 +2971,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); @@ -2513,13 +3005,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()) { @@ -2532,7 +3023,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()) { @@ -2545,7 +3036,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()) { @@ -2558,7 +3049,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()) { @@ -2571,7 +3062,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()) { @@ -2585,7 +3076,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()) { @@ -2598,7 +3089,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()) { @@ -2613,7 +3104,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) { @@ -2626,41 +3117,53 @@ 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); +#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 = 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; @@ -2672,7 +3175,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)) @@ -2691,7 +3195,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; @@ -2700,7 +3204,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)) @@ -2716,7 +3220,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) { @@ -2724,7 +3228,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) { @@ -2738,81 +3242,828 @@ bool ue_py_convert_pyobject(PyObject* py_obj, UProperty* prop, uint8* buffer, in return false; } - - -// check if a python object is a wrapper to a UObject -ue_PyUObject* ue_is_pyuobject(PyObject* obj) +#else +// convert a property to a python object +PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) { - if (!PyObject_IsInstance(obj, (PyObject*)& ue_PyUObjectType)) - return nullptr; - return (ue_PyUObject*)obj; -} + if (auto casted_prop = Cast(prop)) + { + bool value = casted_prop->GetPropertyValue_InContainer(buffer, index); + if (value) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; + } -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)) + { + int value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromLong(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(); + uint32 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromUnsignedLong(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; - } - } + long long value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromLongLong(value); + } - if (!found) + if (auto casted_prop = Cast(prop)) + { + uint64 value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyLong_FromUnsignedLongLong(value); + } + + if (auto casted_prop = Cast(prop)) + { + float value = casted_prop->GetPropertyValue_InContainer(buffer, index); + return PyFloat_FromDouble(value); + } + + 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(); @@ -2879,12 +4130,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) @@ -2908,9 +4168,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); @@ -2943,10 +4211,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); @@ -2974,7 +4250,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++; } @@ -2991,10 +4271,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); @@ -3016,11 +4304,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) @@ -3053,17 +4350,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 @@ -3082,6 +4393,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); @@ -3090,6 +4411,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) @@ -3102,15 +4424,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); @@ -3119,7 +4450,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")); @@ -3133,11 +4469,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")); @@ -3205,8 +4550,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 @@ -3229,6 +4579,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)) { @@ -3354,6 +4833,7 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ prop = prop_struct; } } +#endif if (prop) { @@ -3377,6 +4857,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)) @@ -3503,6 +5107,7 @@ UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_ prop = prop_struct; } } +#endif if (prop) { @@ -3528,33 +5133,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; @@ -3568,10 +5205,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..42492fed9 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 *); @@ -70,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/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/UEPyActor.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyActor.cpp index e319bcc94..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/UEPyCapture.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyCapture.cpp index 26f2194c4..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); @@ -118,8 +127,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); @@ -147,7 +164,50 @@ 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); +#endif return false; } @@ -160,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) @@ -178,6 +242,7 @@ struct FInEditorMultiCapture : TSharedFromThis FSlateApplication::Get().AddWindow(CustomWindow); PlayInEditorSettings->CustomPIEWindow = CustomWindow; +#endif // Reset everything else PlayInEditorSettings->GameGetsMouseControl = false; @@ -196,7 +261,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); } @@ -278,8 +348,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/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index c958094f6..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) { @@ -511,7 +514,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 +553,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/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 3b2983b99..9929cb79a 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) { @@ -647,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) { @@ -660,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) { @@ -801,6 +828,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 +848,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 +877,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 +922,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 +967,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 +1061,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 +1077,7 @@ PyObject *py_ue_properties(ue_PyUObject *self, PyObject * args) PyList_Append(ret, property_name); Py_DECREF(property_name); } +#endif return ret; } @@ -1078,11 +1155,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 +1180,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 +1236,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 +1269,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 +1306,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 +1381,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,7 +1455,27 @@ PyObject *py_ue_get_uproperty(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(u_property); } +#endif + +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_inner(ue_PyFProperty *self, PyObject * args) +{ + + // 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); + + FArrayProperty *f_property = ue_py_check_ftype(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 PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) { @@ -1315,7 +1491,27 @@ PyObject *py_ue_get_inner(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(inner); } +#endif + +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_key_prop(ue_PyFProperty *self, PyObject * args) +{ + + // 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); + + FMapProperty *f_property = ue_py_check_ftype(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 PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) { @@ -1331,7 +1527,27 @@ PyObject *py_ue_get_key_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(key); } +#endif + +#if ENGINE_MINOR_VERSION >= 25 +PyObject *py_ue_get_value_prop(ue_PyFProperty *self, PyObject * args) +{ + + // 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); + FMapProperty *f_property = ue_py_check_ftype(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 PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) { @@ -1347,6 +1563,7 @@ PyObject *py_ue_get_value_prop(ue_PyUObject *self, PyObject * args) Py_RETURN_UOBJECT(value); } +#endif PyObject *py_ue_has_property(ue_PyUObject *self, PyObject * args) { @@ -1370,9 +1587,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 +1621,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 +1789,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 +1805,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 +1855,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 +2639,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 +2664,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..15f902f1d 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -24,10 +24,17 @@ 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 *); +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 *); 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 *); @@ -57,7 +64,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/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) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp new file mode 100644 index 000000000..ebd8d6dbc --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.cpp @@ -0,0 +1,219 @@ + +#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; +} + +#if WITH_EDITOR +// strictly WITH_EDITORONLY_DATA +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 + +#endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h new file mode 100644 index 000000000..64a0e5177 --- /dev/null +++ b/Source/UnrealEnginePython/Private/UObject/UEPyProperty.h @@ -0,0 +1,17 @@ +#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); +#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 + diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 8858a7936..8d5e5bf3e 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -8,6 +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" @@ -166,7 +169,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(); @@ -209,9 +216,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 +234,23 @@ 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 +#if ENGINE_MINOR_VERSION >= 24 + GEditor->GetEditorSubsystem()->OpenEditorForAsset(seq); +#else FAssetEditorManager::Get().OpenEditorForAsset(seq); +#endif } +#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; @@ -464,9 +481,17 @@ 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 + +#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; @@ -519,9 +544,17 @@ 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 + +#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) { @@ -568,9 +601,17 @@ 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 + +#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"); @@ -1659,7 +1700,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/UEPyStaticMesh.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp index eaf1a1cf8..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/UEPyTransform.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyTransform.cpp index 34ae1eade..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"); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyViewport.cpp index 4e7244b63..ab9aafce2 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,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; } @@ -81,14 +100,30 @@ PyObject *py_unreal_engine_editor_set_camera_speed(PyObject * self, PyObject * a return NULL; } + +#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; } @@ -107,14 +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; } @@ -133,14 +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; } diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index 211124849..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" @@ -47,7 +66,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 +267,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 +285,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 +303,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 +320,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 +425,7 @@ void FUnrealEnginePythonModule::StartupModule() } // Setup our own paths for PYTHONPATH + #if PLATFORM_WINDOWS TArray OurPythonPaths = { PythonHome, FPaths::Combine(PythonHome, TEXT("Lib")), @@ -403,10 +438,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/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/UEPyFRotator.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp index 3558faa5b..61646bd29 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; 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 *); 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]); } 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 diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index a6fe71946..0aea1acfd 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", @@ -95,8 +107,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 +226,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 +242,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) @@ -246,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); @@ -259,14 +273,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 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() 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