diff --git a/.gitignore b/.gitignore index a8f0b03d6..e43464f53 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,7 @@ ENV/ .ropeproject Binaries/ +Intermediate/ python35/ python27/ *.un~ diff --git a/README.md b/README.md index 072733a46..68ed706f1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,16 @@ Funny snippets for working with StaticMesh and SkeletalMesh assets: https://gith More tutorials: https://github.com/20tab/UnrealEnginePython/tree/master/tutorials +# Project Status (IMPORTANT) + +Currently (as april 2020) the project is on hold: between 2016 and 2018 20tab invested lot of resources in it but unfortunately epic (during 2018) decided to suddenly release its own implementation and the request made for a megagrant in 2019 by the original plugin author was rejected too. + +As this plugin (still) has way more features than the Epic one and many contributors, **we are currently looking for new maintainers** helping us to keep it alive, checking PR and issues. If you are interested in working on it a few hours a week, drop us a line at info@20tab.com to discuss about it. + +If you are interested in game logic scripting/modding in Unreal Engine 4 consider giving a look at the LuaMachine project (https://github.com/rdeioris/LuaMachine/). + +The plugin should work up to unreal engine version 4.23 and there are forks/pull requests for 4.24. Since 4.25 Epic refactored the UProperty subsystem, so if you want to port the plugin to a version >= 4.25 you should make a lot of search & replace (basically renaming UProperty to FProperty and Cast to CastField should be enough) + # How and Why ? This is a plugin embedding a whole Python VM (versions 3.x [the default and suggested one] and 2.7) In Unreal Engine 4 (both the editor and runtime). @@ -30,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 currently supported Unreal Engine versions are 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19 and 4.20 +The minimal supported Unreal Engine version is 4.12, while the latest is 4.23 We support official python.org releases as well as IntelPython and Anaconda distributions. @@ -943,9 +953,9 @@ We try to do our best to "protect" the user, but you can effectively crash UE fr Contacts and Commercial Support ------------------------------- -If you want to contact us (for help, support, sponsorship), drop a mail to info at 20tab.com or follow @unbit on twitter +If you need commercial support for UnrealEnginePython just drop a mail to info at 20tab.com -We offer commercial support for both UnrealEngine and UnrealEnginePython, again drop a mail to info at 20tab.com for more infos +Follow @unbit on twitter for news about the project Special Thanks -------------- diff --git a/Source/PythonAutomation/Public/PythonAutomationModule.h b/Source/PythonAutomation/Public/PythonAutomationModule.h index c0f9c6d16..bc19a062e 100644 --- a/Source/PythonAutomation/Public/PythonAutomationModule.h +++ b/Source/PythonAutomation/Public/PythonAutomationModule.h @@ -3,7 +3,11 @@ #pragma once #include "CoreMinimal.h" +#if ENGINE_MAJOR_VERSION==4 && ENGINE_MINOR_VERSION>=22 +#include "Modules/ModuleInterface.h" +#else #include "ModuleInterface.h" +#endif class FPythonAutomationModule : public IModuleInterface { diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp index 6f75053ba..a3e53b7c5 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.cpp @@ -381,6 +381,127 @@ PyObject *py_ue_graph_add_node(ue_PyUObject * self, PyObject * args) Py_RETURN_UOBJECT(node); } +PyObject *py_ue_graph_remove_node(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_node_class; + int x = 0; + int y = 0; + + char *metadata = nullptr; + PyObject *py_data = nullptr; + + if (!PyArg_ParseTuple(args, "O|iisO:graph_remove_node", &py_node_class, &x, &y, &metadata, &py_data)) + { + return nullptr; + } + + UEdGraph *graph = ue_py_check_type(self); + if (!graph) + return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); + + UObject *u_obj = ue_py_check_type(py_node_class); + if (!u_obj) + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + + UEdGraphNode *node = nullptr; + + if (UClass *u_class = Cast(u_obj)) + { + if (!u_class->IsChildOf()) + { + return PyErr_Format(PyExc_Exception, "argument is not a child of UEdGraphNode"); + } + node = NewObject(graph, u_class); + node->PostLoad(); + } + else + { + node = Cast(u_obj); + if (node) + { + if (node->GetOuter() != graph) + + node->Rename(*node->GetName(), graph); + } + } + + if (!node) + return PyErr_Format(PyExc_Exception, "argument is not a supported type"); + + graph->RemoveNode(node); + + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); + } + + Py_RETURN_NONE; +} + +PyObject *py_ue_graph_reconstruct_node(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + PyObject *py_node_class; + int x = 0; + int y = 0; + + char *metadata = nullptr; + PyObject *py_data = nullptr; + + if (!PyArg_ParseTuple(args, "O|iisO:graph_reconstruct_node", &py_node_class, &x, &y, &metadata, &py_data)) + { + return nullptr; + } + + UEdGraph *graph = ue_py_check_type(self); + if (!graph) + return PyErr_Format(PyExc_Exception, "uobject is not a UEdGraph"); + + UObject *u_obj = ue_py_check_type(py_node_class); + if (!u_obj) + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + + UEdGraphNode *node = nullptr; + + if (UClass *u_class = Cast(u_obj)) + { + if (!u_class->IsChildOf()) + { + return PyErr_Format(PyExc_Exception, "argument is not a child of UEdGraphNode"); + } + node = NewObject(graph, u_class); + node->PostLoad(); + } + else + { + node = Cast(u_obj); + if (node) + { + if (node->GetOuter() != graph) + + node->Rename(*node->GetName(), graph); + } + } + + if (!node) + return PyErr_Format(PyExc_Exception, "argument is not a supported type"); + + //graph->RemoveNode(node); + node->ReconstructNode(); + + if (UBlueprint *bp = Cast(node->GetGraph()->GetOuter())) + { + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); + } + + Py_RETURN_NONE; +} + PyObject *py_ue_graph_add_node_dynamic_cast(ue_PyUObject * self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h index 03061426b..e0fd0fe69 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraph.h @@ -16,6 +16,9 @@ PyObject *py_ue_graph_add_node(ue_PyUObject *, PyObject *); PyObject *py_ue_graph_add_node_dynamic_cast(ue_PyUObject *, PyObject *); PyObject *py_ue_graph_add_node_event(ue_PyUObject *, PyObject *); +PyObject *py_ue_graph_reconstruct_node(ue_PyUObject *, PyObject *); +PyObject *py_ue_graph_remove_node(ue_PyUObject *, PyObject *); + PyObject *py_ue_graph_get_good_place_for_new_node(ue_PyUObject *, PyObject *); PyObject *py_ue_node_pins(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp index dca7bf864..283079f31 100644 --- a/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp +++ b/Source/UnrealEnginePython/Private/Blueprint/UEPyEdGraphPin.cpp @@ -80,9 +80,49 @@ static PyObject *py_ue_edgraphpin_break_link_to(ue_PyEdGraphPin *self, PyObject Py_RETURN_NONE; } +static PyObject *py_ue_edgraphpin_break_all_pin_links(ue_PyEdGraphPin *self, PyObject * args) +{ + PyObject *py_notify_nodes = nullptr; + if (!PyArg_ParseTuple(args, "O:break_all_pin_links", &py_notify_nodes)) + { + return NULL; + } + + bool notify_nodes = true; + if (py_notify_nodes && !PyObject_IsTrue(py_notify_nodes)) + notify_nodes = false; + + self->pin->BreakAllPinLinks(notify_nodes); + + if (UBlueprint *bp = Cast(self->pin->GetOwningNode()->GetGraph()->GetOuter())) + { + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(bp); + } + + Py_RETURN_NONE; +} + +static PyObject *py_ue_edgraphpin_get_linked_to(ue_PyEdGraphPin * self, PyObject * args) +{ + PyObject *pins = PyList_New(0); + + TArray Links = self->pin->LinkedTo; + + for (int32 i = 0; i < Links.Num(); i++) + { + UEdGraphPin *pin = Links[i]; + ue_PyUObject *item = (ue_PyUObject *)py_ue_new_edgraphpin(pin); + if (item) + PyList_Append(pins, (PyObject *)item); + } + return pins; +} + static PyMethodDef ue_PyEdGraphPin_methods[] = { { "make_link_to", (PyCFunction)py_ue_edgraphpin_make_link_to, METH_VARARGS, "" }, { "break_link_to", (PyCFunction)py_ue_edgraphpin_break_link_to, METH_VARARGS, "" }, + { "break_all_pin_links", (PyCFunction)py_ue_edgraphpin_break_all_pin_links, METH_VARARGS, "" }, + { "get_linked_to", (PyCFunction)py_ue_edgraphpin_get_linked_to, METH_VARARGS, "" }, { "connect", (PyCFunction)py_ue_edgraphpin_connect, METH_VARARGS, "" }, { NULL } /* Sentinel */ }; diff --git a/Source/UnrealEnginePython/Private/PyCommandlet.cpp b/Source/UnrealEnginePython/Private/PyCommandlet.cpp index 9b22ae818..701f05348 100644 --- a/Source/UnrealEnginePython/Private/PyCommandlet.cpp +++ b/Source/UnrealEnginePython/Private/PyCommandlet.cpp @@ -91,7 +91,11 @@ int32 UPyCommandlet::Main(const FString& CommandLine) #if PY_MAJOR_VERSION >= 3 argv[i] = (wchar_t*)malloc(PyArgv[i].Len() + 1); #if PLATFORM_MAC || PLATFORM_LINUX + #if ENGINE_MINOR_VERSION >= 20 + wcsncpy(argv[i], (const wchar_t *) TCHAR_TO_WCHAR(*PyArgv[i].ReplaceEscapedCharWithChar()), PyArgv[i].Len() + 1); + #else wcsncpy(argv[i], *PyArgv[i].ReplaceEscapedCharWithChar(), PyArgv[i].Len() + 1); + #endif #elif PLATFORM_ANDROID wcsncpy(argv[i], (const wchar_t *)*PyArgv[i].ReplaceEscapedCharWithChar(), PyArgv[i].Len() + 1); #else diff --git a/Source/UnrealEnginePython/Private/PythonDelegate.cpp b/Source/UnrealEnginePython/Private/PythonDelegate.cpp index 0ed596d4b..e7e4c6699 100644 --- a/Source/UnrealEnginePython/Private/PythonDelegate.cpp +++ b/Source/UnrealEnginePython/Private/PythonDelegate.cpp @@ -1,6 +1,7 @@ #include "PythonDelegate.h" #include "UEPyModule.h" +#include "UEPyCallable.h" UPythonDelegate::UPythonDelegate() { @@ -101,6 +102,12 @@ void UPythonDelegate::PyInputAxisHandler(float value) Py_DECREF(ret); } +bool UPythonDelegate::UsesPyCallable(PyObject *other) +{ + ue_PyCallable *other_callable = (ue_PyCallable*)other; + ue_PyCallable *this_callable = (ue_PyCallable*)py_callable; + return other_callable->u_function == this_callable->u_function && other_callable->u_target == this_callable->u_target; +} UPythonDelegate::~UPythonDelegate() { @@ -110,4 +117,4 @@ UPythonDelegate::~UPythonDelegate() #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("PythonDelegate %p callable XDECREF'ed"), this); #endif -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/PythonHouseKeeper.cpp b/Source/UnrealEnginePython/Private/PythonHouseKeeper.cpp new file mode 100644 index 000000000..dc014e386 --- /dev/null +++ b/Source/UnrealEnginePython/Private/PythonHouseKeeper.cpp @@ -0,0 +1,254 @@ + +#include "PythonHouseKeeper.h" + +void FUnrealEnginePythonHouseKeeper::AddReferencedObjects(FReferenceCollector& InCollector) +{ + InCollector.AddReferencedObjects(PythonTrackedObjects); +} + +FUnrealEnginePythonHouseKeeper *FUnrealEnginePythonHouseKeeper::Get() +{ + static FUnrealEnginePythonHouseKeeper *Singleton; + if (!Singleton) + { + Singleton = new FUnrealEnginePythonHouseKeeper(); + // register a new delegate for the GC +#if ENGINE_MINOR_VERSION >= 18 + FCoreUObjectDelegates::GetPostGarbageCollect().AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); +#else + FCoreUObjectDelegates::PostGarbageCollect.AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); +#endif + } + return Singleton; +} + +void FUnrealEnginePythonHouseKeeper::RunGCDelegate() +{ + FScopePythonGIL gil; + RunGC(); +} + +int32 FUnrealEnginePythonHouseKeeper::RunGC() +{ + int32 Garbaged = PyUObjectsGC(); + Garbaged += DelegatesGC(); + return Garbaged; +} + +bool FUnrealEnginePythonHouseKeeper::IsValidPyUObject(ue_PyUObject *PyUObject) +{ + if (!PyUObject) + return false; + + UObject *Object = PyUObject->ue_object; + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return false; + } + + if (!Tracker->Owner.IsValid()) + return false; + + return true; + +} + +void FUnrealEnginePythonHouseKeeper::TrackUObject(UObject *Object) +{ + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return; + } + if (Tracker->bPythonOwned) + return; + Tracker->bPythonOwned = true; + // when a new ue_PyUObject spawns, it has a reference counting of two + Py_DECREF(Tracker->PyUObject); + Tracker->PyUObject->owned = 1; + PythonTrackedObjects.Add(Object); +} + +void FUnrealEnginePythonHouseKeeper::UntrackUObject(UObject *Object) +{ + PythonTrackedObjects.Remove(Object); +} + +void FUnrealEnginePythonHouseKeeper::RegisterPyUObject(UObject *Object, ue_PyUObject *InPyUObject) +{ + UObjectPyMapping.Add(Object, FPythonUOjectTracker(Object, InPyUObject)); +} + +void FUnrealEnginePythonHouseKeeper::UnregisterPyUObject(UObject *Object) +{ + UObjectPyMapping.Remove(Object); +} + +ue_PyUObject *FUnrealEnginePythonHouseKeeper::GetPyUObject(UObject *Object) +{ + FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); + if (!Tracker) + { + return nullptr; + } + + if (!Tracker->Owner.IsValid(true)) + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("DEFREF'ing UObject at %p (refcnt: %d)"), Object, Tracker->PyUObject->ob_base.ob_refcnt); +#endif + if (!Tracker->bPythonOwned) + Py_DECREF((PyObject *)Tracker->PyUObject); + UnregisterPyUObject(Object); + return nullptr; +} + + return Tracker->PyUObject; +} + +uint32 FUnrealEnginePythonHouseKeeper::PyUObjectsGC() +{ + uint32 Garbaged = 0; + TArray BrokenList; + for (auto &UObjectPyItem : UObjectPyMapping) + { + UObject *Object = UObjectPyItem.Key; + FPythonUOjectTracker &Tracker = UObjectPyItem.Value; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), Object); +#endif + if (!Tracker.Owner.IsValid(true)) + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), Object, Tracker.PyUObject->ob_base.ob_refcnt); +#endif + BrokenList.Add(Object); + Garbaged++; + } + else + { +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), Object, *Object->GetName()); +#endif +} + } + + for (UObject *Object : BrokenList) + { + FPythonUOjectTracker &Tracker = UObjectPyMapping[Object]; + if (!Tracker.bPythonOwned) + Py_DECREF((PyObject *)Tracker.PyUObject); + UnregisterPyUObject(Object); + } + + return Garbaged; + +} + + +int32 FUnrealEnginePythonHouseKeeper::DelegatesGC() +{ + int32 Garbaged = 0; +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Display, TEXT("Garbage collecting %d UObject delegates"), PyDelegatesTracker.Num()); +#endif + for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) + { + FPythonDelegateTracker &Tracker = PyDelegatesTracker[i]; + if (!Tracker.Owner.IsValid(true)) + { + Tracker.Delegate->RemoveFromRoot(); + PyDelegatesTracker.RemoveAt(i); + Garbaged++; + } + + } + +#if defined(UEPY_MEMORY_DEBUG) + UE_LOG(LogPython, Display, TEXT("Garbage collecting %d Slate delegates"), PySlateDelegatesTracker.Num()); +#endif + + for (int32 i = PySlateDelegatesTracker.Num() - 1; i >= 0; --i) + { + FPythonSWidgetDelegateTracker &Tracker = PySlateDelegatesTracker[i]; + if (!Tracker.Owner.IsValid()) + { + PySlateDelegatesTracker.RemoveAt(i); + Garbaged++; + } + + } + return Garbaged; + } + +UPythonDelegate *FUnrealEnginePythonHouseKeeper::FindDelegate(UObject *Owner, PyObject *PyCallable) +{ + for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) + { + FPythonDelegateTracker &Tracker = PyDelegatesTracker[i]; + if (Tracker.Owner.Get() == Owner && Tracker.Delegate->UsesPyCallable(PyCallable)) + return Tracker.Delegate; + } + return nullptr; +} + +UPythonDelegate *FUnrealEnginePythonHouseKeeper::NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature) +{ + UPythonDelegate *Delegate = NewObject(); + + Delegate->AddToRoot(); + Delegate->SetPyCallable(PyCallable); + Delegate->SetSignature(Signature); + + FPythonDelegateTracker Tracker(Delegate, Owner); + PyDelegatesTracker.Add(Tracker); + + return Delegate; +} + +TSharedRef FUnrealEnginePythonHouseKeeper::NewSlateDelegate(TSharedRef Owner, PyObject *PyCallable) +{ + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + FPythonSWidgetDelegateTracker Tracker(Delegate, Owner); + PySlateDelegatesTracker.Add(Tracker); + + return Delegate; +} + +TSharedRef FUnrealEnginePythonHouseKeeper::NewDeferredSlateDelegate(PyObject *PyCallable) +{ + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + return Delegate; +} + +TSharedRef FUnrealEnginePythonHouseKeeper::NewPythonSmartDelegate(PyObject *PyCallable) +{ + TSharedRef Delegate = MakeShareable(new FPythonSmartDelegate()); + Delegate->SetPyCallable(PyCallable); + + PyStaticSmartDelegatesTracker.Add(Delegate); + + return Delegate; +} + +void FUnrealEnginePythonHouseKeeper::TrackDeferredSlateDelegate(TSharedRef Delegate, TSharedRef Owner) +{ + FPythonSWidgetDelegateTracker Tracker(Delegate, Owner); + PySlateDelegatesTracker.Add(Tracker); +} + +TSharedRef FUnrealEnginePythonHouseKeeper::NewStaticSlateDelegate(PyObject *PyCallable) +{ + TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); + Delegate->SetPyCallable(PyCallable); + + PyStaticSlateDelegatesTracker.Add(Delegate); + + return Delegate; +} + diff --git a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp index d0d5a5549..3e0b978ed 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPyFMenuBuilder.cpp @@ -2,10 +2,10 @@ #include "Wrappers/UEPyESlateEnums.h" -static PyObject *py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder* self, PyObject* args) { - char *name; - char *text; + char* name; + char* text; if (!PyArg_ParseTuple(args, "ss:begin_section", &name, &text)) return nullptr; @@ -14,27 +14,31 @@ static PyObject *py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder *self, PyOb Py_RETURN_NONE; } -static PyObject *py_ue_fmenu_builder_end_section(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_end_section(ue_PyFMenuBuilder* self, PyObject* args) { self->menu_builder.EndSection(); Py_RETURN_NONE; } -static PyObject *py_ue_fmenu_builder_make_widget(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_make_widget(ue_PyFMenuBuilder* self, PyObject* args) { - ue_PySWidget *ret = (ue_PySWidget *)PyObject_New(ue_PySWidget, &ue_PySWidgetType); + ue_PySWidget* ret = (ue_PySWidget*)PyObject_New(ue_PySWidget, &ue_PySWidgetType); new (&ret->Widget) TSharedRef(self->menu_builder.MakeWidget()); - return (PyObject *)ret; + return (PyObject*)ret; } -static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder* self, PyObject* args) { - char *label; - char *tooltip; - PyObject *py_callable; - PyObject *py_obj = nullptr; + char* label; + char* tooltip; + PyObject* py_callable; + PyObject* py_obj = nullptr; +#if ENGINE_MINOR_VERSION >= 23 + int ui_action_type = (int)EUserInterfaceActionType::Button; +#else int ui_action_type = EUserInterfaceActionType::Button; +#endif if (!PyArg_ParseTuple(args, "ssO|Oi:add_menu_entry", &label, &tooltip, &py_callable, &py_obj, &ui_action_type)) return nullptr; @@ -58,14 +62,44 @@ static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyO } self->menu_builder.AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler), NAME_None, +#if ENGINE_MINOR_VERSION >= 23 + (EUserInterfaceActionType)ui_action_type); +#else (EUserInterfaceActionType::Type)ui_action_type); +#endif + + Py_RETURN_NONE; +} + +static PyObject* py_ue_fmenu_builder_add_sub_menu(ue_PyFMenuBuilder* self, PyObject* args) +{ + char* label; + char* tooltip; + PyObject* py_callable; + PyObject* py_bool = nullptr; + if (!PyArg_ParseTuple(args, "ssO|O:add_sub_menu", &label, &tooltip, &py_callable, &py_bool)) + return nullptr; + + if (!PyCallable_Check(py_callable)) + { + return PyErr_Format(PyExc_Exception, "argument is not callable"); + } + + + TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewStaticSlateDelegate(py_callable); + + FNewMenuDelegate menu_delegate; + menu_delegate.BindSP(py_delegate, &FPythonSlateDelegate::SubMenuPyBuilder); + + + self->menu_builder.AddSubMenu(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), menu_delegate, (py_bool && PyObject_IsTrue(py_bool)) ? true : false); Py_RETURN_NONE; } -static PyObject *py_ue_fmenu_builder_add_menu_separator(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_add_menu_separator(ue_PyFMenuBuilder* self, PyObject* args) { - char *name = nullptr; + char* name = nullptr; if (!PyArg_ParseTuple(args, "|s:add_menu_separator", &name)) return nullptr; @@ -81,9 +115,9 @@ static PyObject *py_ue_fmenu_builder_add_menu_separator(ue_PyFMenuBuilder *self, } #if WITH_EDITOR -static PyObject *py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder* self, PyObject* args) { - PyObject *py_assets; + PyObject* py_assets; if (!PyArg_ParseTuple(args, "O:add_asset_actions", &py_assets)) return nullptr; @@ -94,10 +128,10 @@ static PyObject *py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder *self, return PyErr_Format(PyExc_Exception, "argument is not iterable"); } - TArray u_objects; - while (PyObject *item = PyIter_Next(py_assets)) + TArray u_objects; + while (PyObject * item = PyIter_Next(py_assets)) { - UObject *u_object = ue_py_check_type(item); + UObject* u_object = ue_py_check_type(item); if (u_object) { u_objects.Add(u_object); @@ -116,7 +150,7 @@ static PyObject *py_ue_fmenu_builder_add_asset_actions(ue_PyFMenuBuilder *self, } #endif -static PyObject *py_ue_fmenu_builder_add_search_widget(ue_PyFMenuBuilder *self, PyObject * args) +static PyObject* py_ue_fmenu_builder_add_search_widget(ue_PyFMenuBuilder* self, PyObject* args) { self->menu_builder.AddSearchWidget(); @@ -130,6 +164,7 @@ static PyMethodDef ue_PyFMenuBuilder_methods[] = { { "add_menu_entry", (PyCFunction)py_ue_fmenu_builder_add_menu_entry, METH_VARARGS, "" }, { "add_menu_separator", (PyCFunction)py_ue_fmenu_builder_add_menu_separator, METH_VARARGS, "" }, { "add_search_widget", (PyCFunction)py_ue_fmenu_builder_add_search_widget, METH_VARARGS, "" }, + { "add_sub_menu", (PyCFunction)py_ue_fmenu_builder_add_sub_menu, METH_VARARGS, "" }, #if WITH_EDITOR { "add_asset_actions", (PyCFunction)py_ue_fmenu_builder_add_asset_actions, METH_VARARGS, "" }, #endif @@ -137,13 +172,13 @@ static PyMethodDef ue_PyFMenuBuilder_methods[] = { }; -static PyObject *ue_PyFMenuBuilder_str(ue_PyFMenuBuilder *self) +static PyObject* ue_PyFMenuBuilder_str(ue_PyFMenuBuilder* self) { return PyUnicode_FromFormat("", &self->menu_builder); } -static void ue_py_fmenu_builder_dealloc(ue_PyFMenuBuilder *self) +static void ue_py_fmenu_builder_dealloc(ue_PyFMenuBuilder* self) { #if PY_MAJOR_VERSION < 3 self->ob_type->tp_free((PyObject*)self); @@ -183,14 +218,14 @@ static PyTypeObject ue_PyFMenuBuilderType = { ue_PyFMenuBuilder_methods, /* tp_methods */ }; -static int ue_py_fmenu_builder_init(ue_PyFMenuBuilder *self, PyObject *args, PyObject *kwargs) +static int ue_py_fmenu_builder_init(ue_PyFMenuBuilder* self, PyObject* args, PyObject* kwargs) { new(&self->menu_builder) FMenuBuilder(true, nullptr); return 0; } -void ue_python_init_fmenu_builder(PyObject *ue_module) +void ue_python_init_fmenu_builder(PyObject* ue_module) { ue_PyFMenuBuilderType.tp_new = PyType_GenericNew; @@ -200,12 +235,12 @@ void ue_python_init_fmenu_builder(PyObject *ue_module) return; Py_INCREF(&ue_PyFMenuBuilderType); - PyModule_AddObject(ue_module, "FMenuBuilder", (PyObject *)&ue_PyFMenuBuilderType); + PyModule_AddObject(ue_module, "FMenuBuilder", (PyObject*)& ue_PyFMenuBuilderType); } -PyObject *py_ue_new_fmenu_builder(FMenuBuilder menu_builder) +PyObject* py_ue_new_fmenu_builder(FMenuBuilder menu_builder) { - ue_PyFMenuBuilder *ret = (ue_PyFMenuBuilder *)PyObject_New(ue_PyFMenuBuilder, &ue_PyFMenuBuilderType); + ue_PyFMenuBuilder* ret = (ue_PyFMenuBuilder*)PyObject_New(ue_PyFMenuBuilder, &ue_PyFMenuBuilderType); new(&ret->menu_builder) FMenuBuilder(menu_builder); - return (PyObject *)ret; + return (PyObject*)ret; } diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySImage.h b/Source/UnrealEnginePython/Private/Slate/UEPySImage.h index fb608ef55..584e14e50 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySImage.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySImage.h @@ -5,7 +5,11 @@ #include "UEPySLeafWidget.h" +#if ENGINE_MINOR_VERSION > 21 +#include "Runtime/SlateCore/Public/Widgets/Images/SImage.h" +#else #include "Runtime/Slate/Public/Widgets/Images/SImage.h" +#endif #include "Runtime/SlateCore/Public/Styling/SlateBrush.h" extern PyTypeObject ue_PySImageType; diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp index a994186a5..92df02d50 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySTextBlock.cpp @@ -70,7 +70,11 @@ static int ue_py_stext_block_init(ue_PySTextBlock *self, PyObject *args, PyObjec ue_py_slate_farguments_float("line_height_percentage", LineHeightPercentage); ue_py_slate_farguments_struct("margin", Margin, FMargin); ue_py_slate_farguments_float("min_desired_width", MinDesiredWidth); +#if ENGINE_MINOR_VERSION >= 23 + ue_py_slate_farguments_event("on_double_clicked", OnDoubleClicked, FPointerEventHandler, OnMouseEvent); +#else ue_py_slate_farguments_event("on_double_clicked", OnDoubleClicked, FOnClicked, OnClicked); +#endif ue_py_slate_farguments_flinear_color("shadow_color_and_opacity", ShadowColorAndOpacity); ue_py_slate_farguments_fvector2d("shadow_offset", ShadowOffset); ue_py_slate_farguments_text("text", Text); diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp index ae0fade73..2e705a303 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp @@ -416,6 +416,7 @@ void FPythonSlateDelegate::MenuPyAssetBuilder(FMenuBuilder &Builder, TArray FPythonSlateDelegate::OnExtendContentBrowserMenu(const TArray& SelectedAssets) { TSharedRef Extender(new FExtender()); @@ -427,6 +428,21 @@ TSharedRef FPythonSlateDelegate::OnExtendContentBrowserMenu(const TAr #endif + +void FPythonSlateDelegate::SubMenuPyBuilder(FMenuBuilder &Builder) +{ + FScopePythonGIL gil; + + PyObject *ret = PyObject_CallFunction(py_callable, (char *)"N", py_ue_new_fmenu_builder(Builder)); + if (!ret) + { + unreal_engine_py_log_error(); + return; + } + Py_DECREF(ret); +} + + TSharedRef FPythonSlateDelegate::OnGenerateWidget(TSharedPtr py_item) { FScopePythonGIL gil; @@ -1004,8 +1020,11 @@ class FPythonSlateCommands : public TCommands virtual void RegisterCommands() override { commands = MakeShareable(new FUICommandList); - +#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 commands->MapAction(command, FExecuteAction::CreateRaw(this, &FPythonSlateCommands::Callback), FCanExecuteAction()); } @@ -1321,6 +1340,7 @@ PyObject *py_unreal_engine_add_menu_bar_extension(PyObject * self, PyObject * ar if (!PyCallable_Check(py_callable)) return PyErr_Format(PyExc_Exception, "argument is not callable"); + TSharedRef *commands = new TSharedRef(new FPythonSlateCommands()); commands->Get().Setup(command_name, py_callable); @@ -1332,8 +1352,7 @@ PyObject *py_unreal_engine_add_menu_bar_extension(PyObject * self, PyObject * ar ExtensibleModule.GetMenuExtensibilityManager()->AddExtender(extender); - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } PyObject *py_unreal_engine_add_tool_bar_extension(PyObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/Slate/UEPySlateDelegate.h b/Source/UnrealEnginePython/Private/Slate/UEPySlateDelegate.h index f2290e0d8..f74f9fe34 100644 --- a/Source/UnrealEnginePython/Private/Slate/UEPySlateDelegate.h +++ b/Source/UnrealEnginePython/Private/Slate/UEPySlateDelegate.h @@ -50,6 +50,7 @@ class FPythonSlateDelegate : public FPythonSmartDelegate #endif void OnWindowClosed(const TSharedRef &Window); + void SubMenuPyBuilder(FMenuBuilder &Builder); TSharedPtr OnContextMenuOpening(); TSharedRef OnGenerateWidget(TSharedPtr py_item); diff --git a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp index 358bf174d..33982e626 100644 --- a/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp +++ b/Source/UnrealEnginePython/Private/SlateApplication/UEPyFSlateApplication.cpp @@ -157,7 +157,11 @@ static PyObject *py_ue_process_key_char_event(PyObject *cls, PyObject * args) static PyObject *py_ue_create(PyObject *cls, PyObject * args) { #if ENGINE_MINOR_VERSION > 18 +#if ENGINE_MINOR_VERSION > 20 + FSlateApplication::InitHighDPI(true); +#else FSlateApplication::InitHighDPI(); +#endif #endif FSlateApplication::Create(); diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.cpp b/Source/UnrealEnginePython/Private/UEPyEditor.cpp index f23f7c59e..1b3df7bca 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.cpp +++ b/Source/UnrealEnginePython/Private/UEPyEditor.cpp @@ -286,7 +286,7 @@ PyObject *py_unreal_engine_editor_play(PyObject * self, PyObject * args) Py_END_ALLOW_THREADS; Py_RETURN_NONE; - } +} PyObject *py_unreal_engine_editor_select_actor(PyObject * self, PyObject * args) { @@ -462,7 +462,7 @@ PyObject *py_unreal_engine_import_asset(PyObject * self, PyObject * args) } Py_RETURN_NONE; - } +} PyObject *py_unreal_engine_editor_tick(PyObject * self, PyObject * args) { @@ -639,10 +639,11 @@ PyObject *py_unreal_engine_create_asset(PyObject * self, PyObject * args) PyObject *py_unreal_engine_get_asset_referencers(PyObject * self, PyObject * args) { char *path; + int depency_type = (int)EAssetRegistryDependencyType::All; - if (!PyArg_ParseTuple(args, "s:get_asset_referencers", &path)) + if (!PyArg_ParseTuple(args, "s|i:get_asset_referencers", &path, &depency_type)) { - return NULL; + return nullptr; } if (!GEditor) @@ -650,7 +651,7 @@ PyObject *py_unreal_engine_get_asset_referencers(PyObject * self, PyObject * arg FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked("AssetRegistry"); TArray referencers; - AssetRegistryModule.Get().GetReferencers(UTF8_TO_TCHAR(path), referencers); + AssetRegistryModule.Get().GetReferencers(UTF8_TO_TCHAR(path), referencers, (EAssetRegistryDependencyType::Type) depency_type); PyObject *referencers_list = PyList_New(0); for (FName name : referencers) @@ -660,11 +661,38 @@ PyObject *py_unreal_engine_get_asset_referencers(PyObject * self, PyObject * arg return referencers_list; } +PyObject *py_unreal_engine_get_asset_identifier_referencers(PyObject * self, PyObject * args) +{ + char *path; + int depency_type = (int)EAssetRegistryDependencyType::All; + + if (!PyArg_ParseTuple(args, "s|i:get_asset_identifier_referencers", &path, &depency_type)) + { + return nullptr; + } + + if (!GEditor) + return PyErr_Format(PyExc_Exception, "no GEditor found"); + + FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked("AssetRegistry"); + TArray referencers; + AssetRegistryModule.Get().GetReferencers(FAssetIdentifier::FromString(UTF8_TO_TCHAR(path)), referencers, (EAssetRegistryDependencyType::Type) depency_type); + + PyObject *referencers_list = PyList_New(0); + for (FAssetIdentifier identifier : referencers) + { + PyList_Append(referencers_list, PyUnicode_FromString(TCHAR_TO_UTF8(*identifier.ToString()))); + } + return referencers_list; +} + + PyObject *py_unreal_engine_get_asset_dependencies(PyObject * self, PyObject * args) { char *path; + int depency_type = (int)EAssetRegistryDependencyType::All; - if (!PyArg_ParseTuple(args, "s:get_asset_dependencies", &path)) + if (!PyArg_ParseTuple(args, "s|i:get_asset_dependencies", &path, &depency_type)) { return NULL; } @@ -674,7 +702,7 @@ PyObject *py_unreal_engine_get_asset_dependencies(PyObject * self, PyObject * ar FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked("AssetRegistry"); TArray dependencies; - AssetRegistryModule.Get().GetDependencies(UTF8_TO_TCHAR(path), dependencies); + AssetRegistryModule.Get().GetDependencies(UTF8_TO_TCHAR(path), dependencies, (EAssetRegistryDependencyType::Type) depency_type); PyObject *dependencies_list = PyList_New(0); for (FName name : dependencies) @@ -697,6 +725,19 @@ PyObject *py_unreal_engine_get_long_package_path(PyObject * self, PyObject * arg return PyUnicode_FromString(TCHAR_TO_UTF8(*(package_path))); } +PyObject *py_unreal_engine_get_long_package_asset_name(PyObject * self, PyObject * args) +{ + char *path; + if (!PyArg_ParseTuple(args, "s:get_long_package_asset_name", &path)) + { + return NULL; + } + + const FString asset_name = FPackageName::GetLongPackageAssetName(UTF8_TO_TCHAR(path)); + + return PyUnicode_FromString(TCHAR_TO_UTF8(*(asset_name))); +} + PyObject *py_unreal_engine_rename_asset(PyObject * self, PyObject * args) { char *path; @@ -755,7 +796,7 @@ PyObject *py_unreal_engine_rename_asset(PyObject * self, PyObject * args) #endif Py_RETURN_NONE; - } +} PyObject *py_unreal_engine_duplicate_asset(PyObject * self, PyObject * args) { @@ -1432,6 +1473,37 @@ PyObject *py_unreal_engine_get_blueprint_components(PyObject * self, PyObject * } +PyObject *py_unreal_engine_remove_component_from_blueprint(PyObject *self, PyObject *args) +{ + PyObject *py_blueprint; + char *name; + char *parentName = nullptr; + + if (!PyArg_ParseTuple(args, "Os|s:remove_component_from_blueprint", &py_blueprint, &name, &parentName)) + { + return NULL; + } + + if (!ue_is_pyuobject(py_blueprint)) + { + return PyErr_Format(PyExc_Exception, "argument is not a UObject"); + } + + ue_PyUObject *py_obj = (ue_PyUObject *)py_blueprint; + if (!py_obj->ue_object->IsA()) + return PyErr_Format(PyExc_Exception, "uobject is not a UBlueprint"); + UBlueprint *bp = (UBlueprint *)py_obj->ue_object; + + bp->Modify(); + USCS_Node *ComponentNode = bp->SimpleConstructionScript->FindSCSNode(UTF8_TO_TCHAR(name)); + if (ComponentNode) + { + bp->SimpleConstructionScript->RemoveNode(ComponentNode); + } + + Py_RETURN_NONE; +} + PyObject *py_unreal_engine_add_component_to_blueprint(PyObject * self, PyObject * args) { @@ -1838,7 +1910,11 @@ PyObject *py_unreal_engine_editor_on_asset_post_import(PyObject * self, PyObject return PyErr_Format(PyExc_Exception, "object is not a callable"); TSharedRef py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewPythonSmartDelegate(py_callable); +#if ENGINE_MINOR_VERSION > 21 + GEditor->GetEditorSubsystem()->OnAssetPostImport.AddSP(py_delegate, &FPythonSmartDelegate::PyFOnAssetPostImport); +#else FEditorDelegates::OnAssetPostImport.AddSP(py_delegate, &FPythonSmartDelegate::PyFOnAssetPostImport); +#endif Py_RETURN_NONE; } @@ -2074,7 +2150,7 @@ PyObject *py_unreal_engine_add_level_to_world(PyObject *self, PyObject * args) #endif Py_RETURN_UOBJECT(level_streaming); - } +} PyObject *py_unreal_engine_move_selected_actors_to_level(PyObject *self, PyObject * args) { @@ -2492,7 +2568,11 @@ PyObject *py_unreal_engine_unregister_settings(PyObject * self, PyObject * args) PyObject *py_unreal_engine_all_viewport_clients(PyObject * self, PyObject * args) { +#if ENGINE_MINOR_VERSION > 21 + TArray clients = GEditor->GetAllViewportClients(); +#else TArray clients = GEditor->AllViewportClients; +#endif PyObject *py_list = PyList_New(0); for (FEditorViewportClient *client : clients) { @@ -2606,5 +2686,6 @@ PyObject *py_unreal_engine_export_assets(PyObject * self, PyObject * args) Py_RETURN_NONE; } + #endif diff --git a/Source/UnrealEnginePython/Private/UEPyEditor.h b/Source/UnrealEnginePython/Private/UEPyEditor.h index f179c1788..31c8028e1 100644 --- a/Source/UnrealEnginePython/Private/UEPyEditor.h +++ b/Source/UnrealEnginePython/Private/UEPyEditor.h @@ -42,6 +42,7 @@ PyObject *py_unreal_engine_duplicate_asset(PyObject *, PyObject *); PyObject *py_unreal_engine_delete_asset(PyObject *, PyObject *); PyObject *py_unreal_engine_get_long_package_path(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_long_package_asset_name(PyObject *, PyObject *); PyObject *py_unreal_engine_create_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_compile_blueprint(PyObject *, PyObject *); @@ -51,6 +52,7 @@ PyObject *py_unreal_engine_reload_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_replace_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_create_blueprint_from_actor(PyObject *, PyObject *); PyObject *py_unreal_engine_add_component_to_blueprint(PyObject *, PyObject *); +PyObject *py_unreal_engine_remove_component_from_blueprint(PyObject *, PyObject *); PyObject *py_unreal_engine_blueprint_add_member_variable(PyObject *, PyObject *); PyObject *py_unreal_engine_blueprint_add_new_timeline(PyObject *, PyObject *); @@ -95,6 +97,7 @@ PyObject *py_unreal_engine_create_material_instance(PyObject *, PyObject *); PyObject *py_unreal_engine_allow_actor_script_execution_in_editor(PyObject *, PyObject *); PyObject *py_unreal_engine_get_asset_referencers(PyObject *, PyObject *); +PyObject *py_unreal_engine_get_asset_identifier_referencers(PyObject *, PyObject *); PyObject *py_unreal_engine_get_asset_dependencies(PyObject *, PyObject *); PyObject *py_unreal_engine_open_editor_for_asset(PyObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPyModule.cpp b/Source/UnrealEnginePython/Private/UEPyModule.cpp index bfcbcd416..56954d32e 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.cpp +++ b/Source/UnrealEnginePython/Private/UEPyModule.cpp @@ -51,6 +51,7 @@ #include "Wrappers/UEPyESlateEnums.h" #include "Wrappers/UEPyFVector.h" +#include "Wrappers/UEPyFVector2D.h" #include "Wrappers/UEPyFHitResult.h" #include "Wrappers/UEPyFRotator.h" #include "Wrappers/UEPyFTransform.h" @@ -136,7 +137,7 @@ static PyModuleDef unreal_engine_module = { -1, NULL, }; -static PyObject *init_unreal_engine(void); +static PyObject* init_unreal_engine(void); @@ -147,21 +148,21 @@ void init_unreal_engine_builtin() #endif -static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args) +static PyObject* py_unreal_engine_py_gc(PyObject* self, PyObject* args) { int32 Garbaged = FUnrealEnginePythonHouseKeeper::Get()->RunGC(); return PyLong_FromLong(Garbaged); } -static PyObject *py_unreal_engine_exec(PyObject * self, PyObject * args) +static PyObject* py_unreal_engine_exec(PyObject* self, PyObject* args) { - char *filename = nullptr; + char* filename = nullptr; if (!PyArg_ParseTuple(args, "s:exec", &filename)) { return NULL; } - FUnrealEnginePythonModule &PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); + FUnrealEnginePythonModule& PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); Py_BEGIN_ALLOW_THREADS; PythonModule.RunFile(filename); Py_END_ALLOW_THREADS; @@ -170,14 +171,14 @@ static PyObject *py_unreal_engine_exec(PyObject * self, PyObject * args) #if PLATFORM_MAC -static PyObject *py_unreal_engine_exec_in_main_thread(PyObject * self, PyObject * args) +static PyObject* py_unreal_engine_exec_in_main_thread(PyObject* self, PyObject* args) { - char *filename = nullptr; + char* filename = nullptr; if (!PyArg_ParseTuple(args, "s:exec_in_main_thread", &filename)) { return NULL; } - FUnrealEnginePythonModule &PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); + FUnrealEnginePythonModule& PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); Py_BEGIN_ALLOW_THREADS; PythonModule.RunFileInMainThread(filename); Py_END_ALLOW_THREADS; @@ -185,7 +186,7 @@ static PyObject *py_unreal_engine_exec_in_main_thread(PyObject * self, PyObject } #endif -static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) +static PyObject* py_ue_get_py_proxy(ue_PyUObject* self, PyObject* args) { ue_py_check(self); @@ -193,13 +194,13 @@ static PyObject *py_ue_get_py_proxy(ue_PyUObject *self, PyObject * args) if (self->py_proxy) { Py_INCREF(self->py_proxy); - return (PyObject *)self->py_proxy; + return (PyObject*)self->py_proxy; } Py_RETURN_NONE; } -static PyObject *py_unreal_engine_shutdown(PyObject *self, PyObject * args) +static PyObject* py_unreal_engine_shutdown(PyObject* self, PyObject* args) { GIsRequestingExit = true; @@ -207,10 +208,10 @@ static PyObject *py_unreal_engine_shutdown(PyObject *self, PyObject * args) Py_RETURN_NONE; } -static PyObject *py_unreal_engine_set_brutal_finalize(PyObject *self, PyObject * args) +static PyObject* py_unreal_engine_set_brutal_finalize(PyObject* self, PyObject* args) { - PyObject *py_bool = nullptr; + PyObject* py_bool = nullptr; if (!PyArg_ParseTuple(args, "|O:set_brutal_finalize", &py_bool)) { return nullptr; @@ -218,7 +219,7 @@ static PyObject *py_unreal_engine_set_brutal_finalize(PyObject *self, PyObject * bool bBrutalFinalize = !py_bool || PyObject_IsTrue(py_bool); - FUnrealEnginePythonModule &PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); + FUnrealEnginePythonModule& PythonModule = FModuleManager::GetModuleChecked("UnrealEnginePython"); PythonModule.BrutalFinalize = bBrutalFinalize; Py_RETURN_NONE; } @@ -341,6 +342,7 @@ static PyMethodDef unreal_engine_methods[] = { { "sync_browser_to_assets", py_unreal_engine_editor_sync_browser_to_assets, METH_VARARGS, "" }, { "get_asset_referencers", py_unreal_engine_get_asset_referencers, METH_VARARGS, "" }, + { "get_asset_identifier_referencers", py_unreal_engine_get_asset_identifier_referencers, METH_VARARGS, "" }, { "get_asset_dependencies", py_unreal_engine_get_asset_dependencies, METH_VARARGS, "" }, { "rename_asset", py_unreal_engine_rename_asset, METH_VARARGS, "" }, @@ -348,6 +350,7 @@ static PyMethodDef unreal_engine_methods[] = { { "delete_asset", py_unreal_engine_delete_asset, METH_VARARGS, "" }, { "get_long_package_path", py_unreal_engine_get_long_package_path, METH_VARARGS, "" }, + { "get_long_package_asset_name", py_unreal_engine_get_long_package_asset_name, METH_VARARGS, "" }, { "editor_command_build", py_unreal_engine_editor_command_build, METH_VARARGS, "" }, { "editor_command_build_lighting", py_unreal_engine_editor_command_build_lighting, METH_VARARGS, "" }, @@ -386,6 +389,7 @@ static PyMethodDef unreal_engine_methods[] = { { "blueprint_get_all_graphs", py_unreal_engine_blueprint_get_all_graphs, METH_VARARGS, "" }, { "blueprint_mark_as_structurally_modified", py_unreal_engine_blueprint_mark_as_structurally_modified, METH_VARARGS, "" }, { "add_component_to_blueprint", py_unreal_engine_add_component_to_blueprint, METH_VARARGS, "" }, + { "remove_component_from_blueprint", py_unreal_engine_remove_component_from_blueprint, METH_VARARGS, "" }, { "get_blueprint_components", py_unreal_engine_get_blueprint_components, METH_VARARGS, "" }, { "create_material_instance", py_unreal_engine_create_material_instance, METH_VARARGS, "" }, { "message_dialog_open", py_unreal_engine_message_dialog_open, METH_VARARGS, "" }, @@ -604,6 +608,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "set_name", (PyCFunction)py_ue_set_name, METH_VARARGS, "" }, { "bind_event", (PyCFunction)py_ue_bind_event, METH_VARARGS, "" }, + { "unbind_event", (PyCFunction)py_ue_unbind_event, METH_VARARGS, "" }, { "delegate_bind_ufunction", (PyCFunction)py_ue_delegate_bind_ufunction, METH_VARARGS, "" }, { "get_py_proxy", (PyCFunction)py_ue_get_py_proxy, METH_VARARGS, "" }, @@ -616,6 +621,12 @@ static PyMethodDef ue_PyUObject_methods[] = { #if WITH_EDITOR { "get_thumbnail", (PyCFunction)py_ue_get_thumbnail, METH_VARARGS, "" }, { "render_thumbnail", (PyCFunction)py_ue_render_thumbnail, METH_VARARGS, "" }, + {"get_metadata_tag", (PyCFunction)py_ue_get_metadata_tag, METH_VARARGS, "" }, + {"set_metadata_tag", (PyCFunction)py_ue_set_metadata_tag, METH_VARARGS, "" }, + { "metadata_tags", (PyCFunction)py_ue_metadata_tags, METH_VARARGS, "" }, + { "has_metadata_tag", (PyCFunction)py_ue_has_metadata_tag, METH_VARARGS, "" }, + {"remove_metadata_tag", (PyCFunction)py_ue_remove_metadata_tag, METH_VARARGS, "" }, + #endif #if WITH_EDITOR @@ -654,6 +665,9 @@ static PyMethodDef ue_PyUObject_methods[] = { { "graph_add_node_event", (PyCFunction)py_ue_graph_add_node_event, METH_VARARGS, "" }, { "graph_get_good_place_for_new_node", (PyCFunction)py_ue_graph_get_good_place_for_new_node, METH_VARARGS, "" }, + { "graph_reconstruct_node", (PyCFunction)py_ue_graph_reconstruct_node, METH_VARARGS, "" }, + { "graph_remove_node", (PyCFunction)py_ue_graph_remove_node, METH_VARARGS, "" }, + { "node_pins", (PyCFunction)py_ue_node_pins, METH_VARARGS, "" }, { "node_get_title", (PyCFunction)py_ue_node_get_title, METH_VARARGS, "" }, { "node_find_pin", (PyCFunction)py_ue_node_find_pin, METH_VARARGS, "" }, @@ -730,7 +744,9 @@ static PyMethodDef ue_PyUObject_methods[] = { { "get_raw_animation_data", (PyCFunction)py_ue_anim_sequence_get_raw_animation_data, METH_VARARGS, "" }, { "get_raw_animation_track", (PyCFunction)py_ue_anim_sequence_get_raw_animation_track, METH_VARARGS, "" }, { "add_new_raw_track", (PyCFunction)py_ue_anim_sequence_add_new_raw_track, METH_VARARGS, "" }, +#if ENGINE_MINOR_VERSION <23 { "update_compressed_track_map_from_raw", (PyCFunction)py_ue_anim_sequence_update_compressed_track_map_from_raw, METH_VARARGS, "" }, +#endif { "update_raw_track", (PyCFunction)py_ue_anim_sequence_update_raw_track, METH_VARARGS, "" }, { "apply_raw_anim_changes", (PyCFunction)py_ue_anim_sequence_apply_raw_anim_changes, METH_VARARGS, "" }, { "add_key_to_sequence", (PyCFunction)py_ue_anim_add_key_to_sequence, METH_VARARGS, "" }, @@ -743,6 +759,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "vlog_cylinder", (PyCFunction)py_ue_vlog_cylinder, METH_VARARGS, "" }, // StaticMesh + { "get_static_mesh_bounds", (PyCFunction)py_ue_static_mesh_get_bounds, METH_VARARGS, "" }, #if WITH_EDITOR { "static_mesh_build", (PyCFunction)py_ue_static_mesh_build, METH_VARARGS, "" }, { "static_mesh_create_body_setup", (PyCFunction)py_ue_static_mesh_create_body_setup, METH_VARARGS, "" }, @@ -871,6 +888,7 @@ static PyMethodDef ue_PyUObject_methods[] = { { "set_current_level", (PyCFunction)py_ue_set_current_level, METH_VARARGS, "" }, #if WITH_EDITOR + { "get_level_script_blueprint", (PyCFunction)py_ue_get_level_script_blueprint, METH_VARARGS, "" }, { "add_foliage_asset", (PyCFunction)py_ue_add_foliage_asset, METH_VARARGS, "" }, { "get_foliage_instances", (PyCFunction)py_ue_get_foliage_instances, METH_VARARGS, "" }, #endif @@ -1069,7 +1087,11 @@ static PyMethodDef ue_PyUObject_methods[] = { { "sequencer_get_camera_cut_track", (PyCFunction)py_ue_sequencer_get_camera_cut_track, METH_VARARGS, "" }, #if WITH_EDITOR { "sequencer_set_playback_range", (PyCFunction)py_ue_sequencer_set_playback_range, METH_VARARGS, "" }, + { "sequencer_set_view_range", (PyCFunction)py_ue_sequencer_set_view_range, METH_VARARGS, "" }, + { "sequencer_set_working_range", (PyCFunction)py_ue_sequencer_set_working_range, METH_VARARGS, "" }, { "sequencer_set_section_range", (PyCFunction)py_ue_sequencer_set_section_range, METH_VARARGS, "" }, + { "sequencer_get_playback_range", (PyCFunction)py_ue_sequencer_get_playback_range, METH_VARARGS, "" }, + { "sequencer_get_selection_range", (PyCFunction)py_ue_sequencer_get_selection_range, METH_VARARGS, "" }, { "sequencer_folders", (PyCFunction)py_ue_sequencer_folders, METH_VARARGS, "" }, { "sequencer_create_folder", (PyCFunction)py_ue_sequencer_create_folder, METH_VARARGS, "" }, { "sequencer_set_display_name", (PyCFunction)py_ue_sequencer_set_display_name, METH_VARARGS, "" }, @@ -1128,6 +1150,8 @@ static PyMethodDef ue_PyUObject_methods[] = { { "static_mesh_generate_kdop18", (PyCFunction)py_ue_static_mesh_generate_kdop18, METH_VARARGS, "" }, { "static_mesh_generate_kdop26", (PyCFunction)py_ue_static_mesh_generate_kdop26, METH_VARARGS, "" }, + { "static_mesh_import_lod", (PyCFunction)py_ue_static_mesh_import_lod, METH_VARARGS, "" }, + #endif // Viewport @@ -1159,7 +1183,7 @@ static PyMethodDef ue_PyUObject_methods[] = { // destructor -static void ue_pyobject_dealloc(ue_PyUObject *self) +static void ue_pyobject_dealloc(ue_PyUObject* self) { #if defined(UEPY_MEMORY_DEBUG) UE_LOG(LogPython, Warning, TEXT("Destroying ue_PyUObject %p mapped to UObject %p"), self, self->ue_object); @@ -1176,38 +1200,38 @@ static void ue_pyobject_dealloc(ue_PyUObject *self) Py_XDECREF(self->py_dict); - Py_TYPE(self)->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject*)self); } -static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) +static PyObject* ue_PyUObject_getattro(ue_PyUObject* self, PyObject* attr_name) { ue_py_check(self); - PyObject *ret = PyObject_GenericGetAttr((PyObject *)self, attr_name); + PyObject* ret = PyObject_GenericGetAttr((PyObject*)self, attr_name); if (!ret) { if (PyUnicodeOrString_Check(attr_name)) { - const char *attr = UEPyUnicode_AsUTF8(attr_name); + const char* attr = UEPyUnicode_AsUTF8(attr_name); // first check for property - UStruct *u_struct = nullptr; + UStruct* u_struct = nullptr; if (self->ue_object->IsA()) { - u_struct = (UStruct *)self->ue_object; + u_struct = (UStruct*)self->ue_object; } else { - u_struct = (UStruct *)self->ue_object->GetClass(); + u_struct = (UStruct*)self->ue_object->GetClass(); } - UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); + UProperty* u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); if (u_property) { // swallow previous exception PyErr_Clear(); - return ue_py_convert_property(u_property, (uint8 *)self->ue_object, 0); + return ue_py_convert_property(u_property, (uint8*)self->ue_object, 0); } - UFunction *function = self->ue_object->FindFunction(FName(UTF8_TO_TCHAR(attr))); + UFunction* function = self->ue_object->FindFunction(FName(UTF8_TO_TCHAR(attr))); // retry wth K2_ prefix if (!function) { @@ -1220,8 +1244,8 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) { if (self->ue_object->IsA()) { - UClass *u_class = (UClass *)self->ue_object; - UObject *cdo = u_class->GetDefaultObject(); + UClass* u_class = (UClass*)self->ue_object; + UObject* cdo = u_class->GetDefaultObject(); if (cdo) { function = cdo->FindFunction(FName(UTF8_TO_TCHAR(attr))); @@ -1241,7 +1265,7 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) #if ENGINE_MINOR_VERSION >= 15 if (self->ue_object->IsA()) { - UUserDefinedEnum *u_enum = (UUserDefinedEnum *)self->ue_object; + UUserDefinedEnum* u_enum = (UUserDefinedEnum*)self->ue_object; PyErr_Clear(); FString attr_as_string = FString(UTF8_TO_TCHAR(attr)); for (auto item : u_enum->DisplayNameMap) @@ -1260,7 +1284,7 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) #endif if (self->ue_object->IsA()) { - UEnum *u_enum = (UEnum *)self->ue_object; + UEnum* u_enum = (UEnum*)self->ue_object; PyErr_Clear(); #if ENGINE_MINOR_VERSION > 15 int32 value = u_enum->GetIndexByName(FName(UTF8_TO_TCHAR(attr))); @@ -1287,25 +1311,25 @@ static PyObject *ue_PyUObject_getattro(ue_PyUObject *self, PyObject *attr_name) return ret; } -static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObject *value) +static int ue_PyUObject_setattro(ue_PyUObject* self, PyObject* attr_name, PyObject* value) { ue_py_check_int(self); // first of all check for UProperty if (PyUnicodeOrString_Check(attr_name)) { - const char *attr = UEPyUnicode_AsUTF8(attr_name); + const char* attr = UEPyUnicode_AsUTF8(attr_name); // first check for property - UStruct *u_struct = nullptr; + UStruct* u_struct = nullptr; if (self->ue_object->IsA()) { - u_struct = (UStruct *)self->ue_object; + u_struct = (UStruct*)self->ue_object; } else { - u_struct = (UStruct *)self->ue_object->GetClass(); + u_struct = (UStruct*)self->ue_object->GetClass(); } - UProperty *u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); + UProperty* u_property = u_struct->FindPropertyByName(FName(UTF8_TO_TCHAR(attr))); if (u_property) { #if WITH_EDITOR @@ -1319,9 +1343,9 @@ static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObje if (self->ue_object->HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject)) { - TArray Instances; + TArray Instances; self->ue_object->GetArchetypeInstances(Instances); - for (UObject *Instance : Instances) + for (UObject* Instance : Instances) { Instance->PreEditChange(u_property); if (ue_py_convert_pyobject(value, u_property, (uint8*)Instance, 0)) @@ -1350,10 +1374,10 @@ static int ue_PyUObject_setattro(ue_PyUObject *self, PyObject *attr_name, PyObje return -1; } } - return PyObject_GenericSetAttr((PyObject *)self, attr_name, value); + return PyObject_GenericSetAttr((PyObject*)self, attr_name, value); } -static PyObject *ue_PyUObject_str(ue_PyUObject *self) +static PyObject* ue_PyUObject_str(ue_PyUObject* self) { ue_py_check(self); @@ -1366,13 +1390,13 @@ static PyObject *ue_PyUObject_str(ue_PyUObject *self) #endif } -static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject *kw) +static PyObject* ue_PyUObject_call(ue_PyUObject* 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; + UClass* u_class = (UClass*)self->ue_object; if (u_class->HasAnyClassFlags(CLASS_Abstract)) { return PyErr_Format(PyExc_Exception, "abstract classes cannot be instantiated"); @@ -1381,16 +1405,16 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject { return PyErr_Format(PyExc_Exception, "you cannot use __call__ on actors, they have to be spawned"); } - PyObject *py_name = nullptr; - PyObject *py_outer = Py_None; + PyObject* py_name = nullptr; + PyObject* py_outer = Py_None; if (!PyArg_ParseTuple(args, "|OO:new_object", &py_name, &py_outer)) { return NULL; } int num_args = py_name ? 3 : 1; - PyObject *py_args = PyTuple_New(num_args); - Py_INCREF((PyObject *)self); - PyTuple_SetItem(py_args, 0, (PyObject *)self); + PyObject* py_args = PyTuple_New(num_args); + Py_INCREF((PyObject*)self); + PyTuple_SetItem(py_args, 0, (PyObject*)self); if (py_name) { Py_INCREF(py_outer); @@ -1398,7 +1422,7 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject Py_INCREF(py_name); PyTuple_SetItem(py_args, 2, py_name); } - ue_PyUObject *ret = (ue_PyUObject *)py_unreal_engine_new_object(nullptr, py_args); + ue_PyUObject* ret = (ue_PyUObject*)py_unreal_engine_new_object(nullptr, py_args); Py_DECREF(py_args); if (!ret) { @@ -1408,23 +1432,23 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject // UObject crated explicitely from python, will be managed by python... FUnrealEnginePythonHouseKeeper::Get()->TrackUObject(ret->ue_object); - return (PyObject *)ret; + return (PyObject*)ret; } // if it is a uscriptstruct, instantiate a new struct if (self->ue_object->IsA()) { - UScriptStruct *u_script_struct = (UScriptStruct *)self->ue_object; - uint8 *data = (uint8*)FMemory::Malloc(u_script_struct->GetStructureSize()); + UScriptStruct* u_script_struct = (UScriptStruct*)self->ue_object; + uint8* data = (uint8*)FMemory::Malloc(u_script_struct->GetStructureSize()); u_script_struct->InitializeStruct(data); #if WITH_EDITOR u_script_struct->InitializeDefaultValue(data); #endif if (kw) { - PyObject *struct_keys = PyObject_GetIter(kw); + PyObject* struct_keys = PyObject_GetIter(kw); for (;;) { - PyObject *key = PyIter_Next(struct_keys); + PyObject* key = PyIter_Next(struct_keys); if (!key) { if (PyErr_Occurred()) @@ -1436,9 +1460,9 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject } if (!PyUnicodeOrString_Check(key)) continue; - const char *struct_key = UEPyUnicode_AsUTF8(key); + const char* struct_key = UEPyUnicode_AsUTF8(key); - PyObject *value = PyDict_GetItem(kw, key); + PyObject* value = PyDict_GetItem(kw, key); if (!value) { if (PyErr_Occurred()) @@ -1449,7 +1473,7 @@ static PyObject *ue_PyUObject_call(ue_PyUObject *self, PyObject *args, PyObject break; } - UProperty *u_property = ue_struct_get_field_from_name(u_script_struct, (char *)struct_key); + UProperty* u_property = ue_struct_get_field_from_name(u_script_struct, (char*)struct_key); if (u_property) { if (!ue_py_convert_pyobject(value, u_property, data, 0)) @@ -1507,12 +1531,12 @@ static PyTypeObject ue_PyUObjectType = { -UClass *unreal_engine_new_uclass(char *name, UClass *outer_parent) +UClass* unreal_engine_new_uclass(char* name, UClass* outer_parent) { bool is_overwriting = false; - UObject *outer = GetTransientPackage(); - UClass *parent = UObject::StaticClass(); + UObject* outer = GetTransientPackage(); + UClass* parent = UObject::StaticClass(); if (outer_parent) { @@ -1520,7 +1544,7 @@ UClass *unreal_engine_new_uclass(char *name, UClass *outer_parent) outer = parent->GetOuter(); } - UClass *new_object = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(name)); + UClass* new_object = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR(name)); if (!new_object) { new_object = NewObject(outer, UTF8_TO_TCHAR(name), RF_Public | RF_Transient | RF_MarkAsNative); @@ -1535,13 +1559,13 @@ UClass *unreal_engine_new_uclass(char *name, UClass *outer_parent) if (is_overwriting && new_object->Children) { - UField *u_field = new_object->Children; + UField* u_field = new_object->Children; while (u_field) { if (u_field->IsA()) { UE_LOG(LogPython, Warning, TEXT("removing function %s"), *u_field->GetName()); - new_object->RemoveFunctionFromFunctionMap((UFunction *)u_field); + new_object->RemoveFunctionFromFunctionMap((UFunction*)u_field); FLinkerLoad::InvalidateExport(u_field); } u_field = u_field->Next; @@ -1611,14 +1635,14 @@ UClass *unreal_engine_new_uclass(char *name, UClass *outer_parent) -int unreal_engine_py_init(ue_PyUObject *, PyObject *, PyObject *); +int unreal_engine_py_init(ue_PyUObject*, PyObject*, PyObject*); void unreal_engine_init_py_module() { #if PY_MAJOR_VERSION >= 3 - PyObject *new_unreal_engine_module = PyImport_AddModule("unreal_engine"); + PyObject * new_unreal_engine_module = PyImport_AddModule("unreal_engine"); #else - PyObject *new_unreal_engine_module = Py_InitModule3("unreal_engine", NULL, unreal_engine_py_doc); + 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; @@ -1628,19 +1652,20 @@ void unreal_engine_init_py_module() return; Py_INCREF(&ue_PyUObjectType); - PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject *)&ue_PyUObjectType); + PyModule_AddObject(new_unreal_engine_module, "UObject", (PyObject*)& ue_PyUObjectType); - PyObject *unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); + PyObject* unreal_engine_dict = PyModule_GetDict(new_unreal_engine_module); - PyMethodDef *unreal_engine_function; + PyMethodDef* unreal_engine_function; for (unreal_engine_function = unreal_engine_methods; unreal_engine_function->ml_name != NULL; unreal_engine_function++) { - PyObject *func = PyCFunction_New(unreal_engine_function, NULL); + PyObject* func = PyCFunction_New(unreal_engine_function, NULL); PyDict_SetItemString(unreal_engine_dict, unreal_engine_function->ml_name, func); Py_DECREF(func); } ue_python_init_fvector(new_unreal_engine_module); + ue_python_init_fvector2d(new_unreal_engine_module); ue_python_init_frotator(new_unreal_engine_module); ue_python_init_ftransform(new_unreal_engine_module); ue_python_init_fhitresult(new_unreal_engine_module); @@ -1686,9 +1711,9 @@ void unreal_engine_init_py_module() ue_python_init_enumsimporter(new_unreal_engine_module); ue_python_init_ustructsimporter(new_unreal_engine_module); +#if WITH_EDITOR ue_python_init_ffoliage_instance(new_unreal_engine_module); -#if WITH_EDITOR ue_python_init_fslowtask(new_unreal_engine_module); ue_python_init_swidget(new_unreal_engine_module); ue_python_init_farfilter(new_unreal_engine_module); @@ -1722,9 +1747,9 @@ void unreal_engine_init_py_module() ue_python_init_ivoice_capture(new_unreal_engine_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); + 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); PyDict_SetItemString(unreal_engine_dict, "ENGINE_MAJOR_VERSION", PyLong_FromLong(ENGINE_MAJOR_VERSION)); @@ -1828,18 +1853,18 @@ void unreal_engine_init_py_module() // utility functions -ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) +ue_PyUObject* ue_get_python_uobject(UObject* ue_obj) { if (!ue_obj) return nullptr; - ue_PyUObject *ret = FUnrealEnginePythonHouseKeeper::Get()->GetPyUObject(ue_obj); + ue_PyUObject* ret = FUnrealEnginePythonHouseKeeper::Get()->GetPyUObject(ue_obj); if (!ret) { if (!ue_obj->IsValidLowLevel() || ue_obj->IsPendingKillOrUnreachable()) return nullptr; - ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType); + ue_PyUObject* ue_py_object = (ue_PyUObject*)PyObject_New(ue_PyUObject, &ue_PyUObjectType); if (!ue_py_object) { return nullptr; @@ -1856,14 +1881,14 @@ ue_PyUObject *ue_get_python_uobject(UObject *ue_obj) UE_LOG(LogPython, Warning, TEXT("CREATED UPyObject at %p for %p %s"), ue_py_object, ue_obj, *ue_obj->GetName()); #endif return ue_py_object; - } + } return ret; - } +} -ue_PyUObject *ue_get_python_uobject_inc(UObject *ue_obj) +ue_PyUObject* ue_get_python_uobject_inc(UObject* ue_obj) { - ue_PyUObject *ret = ue_get_python_uobject(ue_obj); + ue_PyUObject* ret = ue_get_python_uobject(ue_obj); if (ret) { Py_INCREF(ret); @@ -1873,9 +1898,9 @@ ue_PyUObject *ue_get_python_uobject_inc(UObject *ue_obj) void unreal_engine_py_log_error() { - PyObject *type = NULL; - PyObject *value = NULL; - PyObject *traceback = NULL; + PyObject* type = NULL; + PyObject* value = NULL; + PyObject* traceback = NULL; PyErr_Fetch(&type, &value, &traceback); PyErr_NormalizeException(&type, &value, &traceback); @@ -1886,9 +1911,9 @@ void unreal_engine_py_log_error() return; } - char *msg = NULL; + char* msg = NULL; #if PY_MAJOR_VERSION >= 3 - PyObject *zero = PyUnicode_AsUTF8String(PyObject_Str(value)); + PyObject * zero = PyUnicode_AsUTF8String(PyObject_Str(value)); if (zero) { msg = PyBytes_AsString(zero); @@ -1911,19 +1936,19 @@ void unreal_engine_py_log_error() return; } - PyObject *traceback_module = PyImport_ImportModule("traceback"); + PyObject* traceback_module = PyImport_ImportModule("traceback"); if (!traceback_module) { PyErr_Clear(); return; } - PyObject *traceback_dict = PyModule_GetDict(traceback_module); - PyObject *format_exception = PyDict_GetItemString(traceback_dict, "format_exception"); + PyObject* traceback_dict = PyModule_GetDict(traceback_module); + PyObject* format_exception = PyDict_GetItemString(traceback_dict, "format_exception"); if (format_exception) { - PyObject *ret = PyObject_CallFunctionObjArgs(format_exception, type, value, traceback, NULL); + PyObject* ret = PyObject_CallFunctionObjArgs(format_exception, type, value, traceback, NULL); if (!ret) { PyErr_Clear(); @@ -1933,7 +1958,7 @@ void unreal_engine_py_log_error() { for (int i = 0; i < PyList_Size(ret); i++) { - PyObject *item = PyList_GetItem(ret, i); + PyObject* item = PyList_GetItem(ret, i); if (item) { UE_LOG(LogPython, Error, TEXT("%s"), UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(PyObject_Str(item)))); @@ -1947,26 +1972,26 @@ void unreal_engine_py_log_error() } PyErr_Clear(); - } +} // retrieve a UWorld from a generic UObject (if possible) -UWorld *ue_get_uworld(ue_PyUObject *py_obj) +UWorld* ue_get_uworld(ue_PyUObject* py_obj) { if (py_obj->ue_object->IsA()) { - return (UWorld *)py_obj->ue_object; + return (UWorld*)py_obj->ue_object; } if (py_obj->ue_object->IsA()) { - AActor *actor = (AActor *)py_obj->ue_object; + AActor* actor = (AActor*)py_obj->ue_object; return actor->GetWorld(); } if (py_obj->ue_object->IsA()) { - UActorComponent *component = (UActorComponent *)py_obj->ue_object; + UActorComponent* component = (UActorComponent*)py_obj->ue_object; return component->GetWorld(); } @@ -1974,23 +1999,23 @@ UWorld *ue_get_uworld(ue_PyUObject *py_obj) } // retrieve actor from component (if possible) -AActor *ue_get_actor(ue_PyUObject *py_obj) +AActor* ue_get_actor(ue_PyUObject* py_obj) { if (py_obj->ue_object->IsA()) { - return (AActor *)py_obj->ue_object; + return (AActor*)py_obj->ue_object; } if (py_obj->ue_object->IsA()) { - UActorComponent *tmp_component = (UActorComponent *)py_obj->ue_object; + UActorComponent* tmp_component = (UActorComponent*)py_obj->ue_object; return tmp_component->GetOwner(); } return nullptr; } // convert a property to a python object -PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) +PyObject* ue_py_convert_property(UProperty* prop, uint8* buffer, int32 index) { if (auto casted_prop = Cast(prop)) { @@ -2041,7 +2066,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) #if ENGINE_MINOR_VERSION >= 15 if (auto casted_prop = Cast(prop)) { - void *prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); + void* prop_addr = casted_prop->ContainerPtrToValuePtr(buffer, index); uint64 enum_index = casted_prop->GetUnderlyingProperty()->GetUnsignedIntPropertyValue(prop_addr); return PyLong_FromUnsignedLong(enum_index); } @@ -2090,12 +2115,16 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) { if (auto casted_struct = Cast(casted_prop->Struct)) { - // check for FVector 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); @@ -2129,7 +2158,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) if (auto casted_prop = Cast(prop)) { auto value = casted_prop->GetPropertyValue_InContainer(buffer, index); - UObject *strong_obj = value.Get(); + UObject* strong_obj = value.Get(); if (strong_obj) { Py_RETURN_UOBJECT(strong_obj); @@ -2152,20 +2181,20 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) { FScriptArrayHelper_InContainer array_helper(casted_prop, buffer, index); - UProperty *array_prop = casted_prop->Inner; + 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()); + uint8* buf = array_helper.GetRawPtr(); + return PyByteArray_FromStringAndSize((char*)buf, array_helper.Num()); } - PyObject *py_list = PyList_New(0); + 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); + PyObject* item = ue_py_convert_property(array_prop, array_helper.GetRawPtr(i), 0); if (!item) { Py_DECREF(py_list); @@ -2183,23 +2212,23 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) { FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); - PyObject *py_dict = PyDict_New(); + 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); + uint8* ptr = map_helper.GetPairPtr(i); - PyObject *py_key = ue_py_convert_property(map_helper.KeyProp, ptr, 0); + 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); + PyObject* py_value = ue_py_convert_property(map_helper.ValueProp, ptr, 0); if (!py_value) { Py_DECREF(py_dict); @@ -2220,7 +2249,7 @@ PyObject *ue_py_convert_property(UProperty *prop, uint8 *buffer, int32 index) } // 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, UProperty* prop, uint8* buffer, int32 index) { if (PyBool_Check(py_obj)) @@ -2243,42 +2272,42 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { if (auto casted_prop = Cast(prop)) { - PyObject *py_long = PyNumber_Long(py_obj); + 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); + 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); + 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); + 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); + 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); + PyObject* py_long = PyNumber_Long(py_obj); casted_prop->SetPropertyValue_InContainer(buffer, PyLong_AsUnsignedLong(py_long), index); Py_DECREF(py_long); return true; @@ -2286,8 +2315,8 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in #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); + 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; @@ -2328,7 +2357,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { Py_ssize_t pybytes_len = PyBytes_Size(py_obj); - uint8 *buf = (uint8 *)PyBytes_AsString(py_obj); + uint8* buf = (uint8*)PyBytes_AsString(py_obj); // fix array helper size @@ -2360,7 +2389,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { Py_ssize_t pybytes_len = PyByteArray_Size(py_obj); - uint8 *buf = (uint8 *)PyByteArray_AsString(py_obj); + uint8* buf = (uint8*)PyByteArray_AsString(py_obj); // fix array helper size @@ -2389,7 +2418,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); - UProperty *array_prop = casted_prop->Inner; + UProperty* array_prop = casted_prop->Inner; Py_ssize_t pylist_len = PyList_Size(py_obj); // fix array helper size @@ -2404,7 +2433,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in for (int i = 0; i < (int)pylist_len; i++) { - PyObject *py_item = PyList_GetItem(py_obj, i); + PyObject* py_item = PyList_GetItem(py_obj, i); if (!ue_py_convert_pyobject(py_item, array_prop, helper.GetRawPtr(i), 0)) { return false; @@ -2422,7 +2451,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { FScriptArrayHelper_InContainer helper(casted_prop, buffer, index); - UProperty *array_prop = casted_prop->Inner; + UProperty* array_prop = casted_prop->Inner; Py_ssize_t pytuple_len = PyTuple_Size(py_obj); // fix array helper size @@ -2437,7 +2466,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in for (int i = 0; i < (int)pytuple_len; i++) { - PyObject *py_item = PyTuple_GetItem(py_obj, i); + PyObject* py_item = PyTuple_GetItem(py_obj, i); if (!ue_py_convert_pyobject(py_item, array_prop, helper.GetRawPtr(i), 0)) { return false; @@ -2456,8 +2485,8 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { FScriptMapHelper_InContainer map_helper(casted_prop, buffer, index); - PyObject *py_key = nullptr; - PyObject *py_value = nullptr; + PyObject* py_key = nullptr; + PyObject* py_value = nullptr; Py_ssize_t pos = 0; map_helper.EmptyValues(); @@ -2465,7 +2494,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { int32 hindex = map_helper.AddDefaultValue_Invalid_NeedsRehash(); - uint8 *ptr = map_helper.GetPairPtr(hindex); + uint8* ptr = map_helper.GetPairPtr(hindex); if (!ue_py_convert_pyobject(py_key, casted_prop->KeyProp, ptr, 0)) { @@ -2488,7 +2517,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in // structs - if (ue_PyFVector *py_vec = py_ue_is_fvector(py_obj)) + if (ue_PyFVector * py_vec = py_ue_is_fvector(py_obj)) { if (auto casted_prop = Cast(prop)) { @@ -2501,7 +2530,20 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in return false; } - if (ue_PyFRotator *py_rot = py_ue_is_frotator(py_obj)) + 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)) { @@ -2514,7 +2556,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in return false; } - if (ue_PyFTransform *py_transform = py_ue_is_ftransform(py_obj)) + if (ue_PyFTransform * py_transform = py_ue_is_ftransform(py_obj)) { if (auto casted_prop = Cast(prop)) { @@ -2527,7 +2569,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in return false; } - if (ue_PyFColor *py_color = py_ue_is_fcolor(py_obj)) + if (ue_PyFColor * py_color = py_ue_is_fcolor(py_obj)) { if (auto casted_prop = Cast(prop)) { @@ -2541,7 +2583,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in return false; } - if (ue_PyFLinearColor *py_color = py_ue_is_flinearcolor(py_obj)) + if (ue_PyFLinearColor * py_color = py_ue_is_flinearcolor(py_obj)) { if (auto casted_prop = Cast(prop)) { @@ -2554,7 +2596,7 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in return false; } - if (ue_PyFHitResult *py_hit = py_ue_is_fhitresult(py_obj)) + if (ue_PyFHitResult * py_hit = py_ue_is_fhitresult(py_obj)) { if (auto casted_prop = Cast(prop)) { @@ -2570,22 +2612,23 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in // generic structs if (py_ue_is_uscriptstruct(py_obj)) { - ue_PyUScriptStruct *py_u_struct = (ue_PyUScriptStruct *)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); - FMemory::Memcpy(dest, py_u_struct->u_struct_ptr, py_u_struct->u_struct->GetStructureSize()); + 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)) + if (PyObject_IsInstance(py_obj, (PyObject*)& ue_PyUObjectType)) { - ue_PyUObject *ue_obj = (ue_PyUObject *)py_obj; + ue_PyUObject* ue_obj = (ue_PyUObject*)py_obj; if (ue_obj->ue_object->IsA()) { if (auto casted_prop = Cast(prop)) @@ -2631,9 +2674,18 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in { if (auto casted_prop = Cast(prop)) { - // ensure the object type is correct, otherwise crash could happen (soon or later) - if (!ue_obj->ue_object->IsA(casted_prop->PropertyClass)) - return false; + // 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); @@ -2689,24 +2741,24 @@ bool ue_py_convert_pyobject(PyObject *py_obj, UProperty *prop, uint8 *buffer, in // check if a python object is a wrapper to a UObject -ue_PyUObject *ue_is_pyuobject(PyObject *obj) +ue_PyUObject* ue_is_pyuobject(PyObject* obj) { - if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyUObjectType)) + if (!PyObject_IsInstance(obj, (PyObject*)& ue_PyUObjectType)) return nullptr; - return (ue_PyUObject *)obj; + return (ue_PyUObject*)obj; } -void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class) +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); + PyObject* attrs = PyObject_Dir(py_class); if (!attrs) return; - AActor *actor = Cast(u_obj); + AActor* actor = Cast(u_obj); if (!actor) { - UActorComponent *component = Cast(u_obj); + UActorComponent* component = Cast(u_obj); if (!component) return; actor = component->GetOwner(); @@ -2715,14 +2767,14 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class Py_ssize_t len = PyList_Size(attrs); for (Py_ssize_t i = 0; i < len; i++) { - PyObject *py_attr_name = PyList_GetItem(attrs, 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)); + 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"); + PyObject* event_signature = PyObject_GetAttrString(item, (char*)"ue_event"); if (event_signature) { if (PyUnicodeOrString_Check(event_signature)) @@ -2747,7 +2799,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class else { bool found = false; - for (UActorComponent *component : actor->GetComponents()) + for (UActorComponent* component : actor->GetComponents()) { if (component->GetFName() == FName(*parts[0])) { @@ -2784,23 +2836,23 @@ void ue_bind_events_for_py_class_by_attribute(UObject *u_obj, PyObject *py_class } // automatically bind events based on class methods names -void ue_autobind_events_for_pyclass(ue_PyUObject *u_obj, PyObject *py_class) +void ue_autobind_events_for_pyclass(ue_PyUObject* u_obj, PyObject* py_class) { - PyObject *attrs = PyObject_Dir(py_class); + PyObject* attrs = PyObject_Dir(py_class); if (!attrs) return; Py_ssize_t len = PyList_Size(attrs); for (Py_ssize_t i = 0; i < len; i++) { - PyObject *py_attr_name = PyList_GetItem(attrs, i); + PyObject* py_attr_name = PyList_GetItem(attrs, i); if (!py_attr_name || !PyUnicodeOrString_Check(py_attr_name)) continue; FString attr_name = UTF8_TO_TCHAR(UEPyUnicode_AsUTF8(py_attr_name)); if (!attr_name.StartsWith("on_", ESearchCase::CaseSensitive)) continue; // check if the attr is a callable - PyObject *item = PyObject_GetAttrString(py_class, TCHAR_TO_UTF8(*attr_name)); + PyObject* item = PyObject_GetAttrString(py_class, TCHAR_TO_UTF8(*attr_name)); if (item && PyCallable_Check(item)) { TArray parts; @@ -2824,24 +2876,24 @@ void ue_autobind_events_for_pyclass(ue_PyUObject *u_obj, PyObject *py_class) Py_DECREF(attrs); } -static void py_ue_destroy_params(UFunction *u_function, uint8 *buffer) +static void py_ue_destroy_params(UFunction* u_function, uint8* buffer) { // destroy params TFieldIterator DArgs(u_function); for (; DArgs && (DArgs->PropertyFlags & CPF_Parm); ++DArgs) { - UProperty *prop = *DArgs; + UProperty* prop = *DArgs; prop->DestroyValue_InContainer(buffer); } } -PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject *args, int argn, PyObject *kwargs) +PyObject* py_ue_ufunction_call(UFunction* u_function, UObject* u_obj, PyObject* args, int argn, PyObject* kwargs) { // check for __super call if (kwargs) { - PyObject *is_super_call = PyDict_GetItemString(kwargs, (char *)"__super"); + PyObject* is_super_call = PyDict_GetItemString(kwargs, (char*)"__super"); if (is_super_call) { if (!u_function->GetSuperFunction()) @@ -2853,12 +2905,12 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } //NOTE: u_function->PropertiesSize maps to local variable uproperties + ufunction paramaters uproperties - uint8 *buffer = (uint8 *)FMemory_Alloca(u_function->ParmsSize); + uint8* buffer = (uint8*)FMemory_Alloca(u_function->ParmsSize); FMemory::Memzero(buffer, u_function->ParmsSize); // initialize args for (TFieldIterator IArgs(u_function); IArgs && IArgs->HasAnyPropertyFlags(CPF_Parm); ++IArgs) { - UProperty *prop = *IArgs; + UProperty* prop = *IArgs; if (!prop->HasAnyPropertyFlags(CPF_ZeroConstructor)) { prop->InitializeValue_InContainer(buffer); @@ -2883,8 +2935,8 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * #endif } #endif - } } + } Py_ssize_t tuple_len = PyTuple_Size(args); @@ -2894,10 +2946,10 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * TFieldIterator PArgs(u_function); for (; PArgs && ((PArgs->PropertyFlags & (CPF_Parm | CPF_ReturnParm)) == CPF_Parm); ++PArgs) { - UProperty *prop = *PArgs; + UProperty* prop = *PArgs; if (argn < tuple_len) { - PyObject *py_arg = PyTuple_GetItem(args, argn); + PyObject* py_arg = PyTuple_GetItem(args, argn); if (!py_arg) { py_ue_destroy_params(u_function, buffer); @@ -2911,8 +2963,8 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * } else if (kwargs) { - char *prop_name = TCHAR_TO_UTF8(*prop->GetName()); - PyObject *dict_value = PyDict_GetItemString(kwargs, prop_name); + char* prop_name = TCHAR_TO_UTF8(*prop->GetName()); + PyObject* dict_value = PyDict_GetItemString(kwargs, prop_name); if (dict_value) { if (!ue_py_convert_pyobject(dict_value, prop, buffer, 0)) @@ -2936,13 +2988,13 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * u_obj->ProcessEvent(u_function, buffer); Py_END_ALLOW_THREADS; - PyObject *ret = nullptr; + PyObject* ret = nullptr; int has_ret_param = 0; TFieldIterator Props(u_function); for (; Props; ++Props) { - UProperty *prop = *Props; + UProperty* prop = *Props; if (prop->GetPropertyFlags() & CPF_ReturnParm) { ret = ue_py_convert_property(prop, buffer, 0); @@ -2959,7 +3011,7 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * if (has_out_params > 0) { - PyObject *multi_ret = PyTuple_New(has_out_params + has_ret_param); + PyObject* multi_ret = PyTuple_New(has_out_params + has_ret_param); if (ret) { PyTuple_SetItem(multi_ret, 0, ret); @@ -2967,13 +3019,13 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * TFieldIterator OProps(u_function); for (; OProps; ++OProps) { - UProperty *prop = *OProps; + UProperty* prop = *OProps; if (prop->HasAnyPropertyFlags(CPF_OutParm) && (prop->IsA() || prop->HasAnyPropertyFlags(CPF_ConstParm) == false)) { // skip return param as it must be always the first if (prop->GetPropertyFlags() & CPF_ReturnParm) continue; - PyObject *py_out = ue_py_convert_property(prop, buffer, 0); + PyObject* py_out = ue_py_convert_property(prop, buffer, 0); if (!py_out) { Py_DECREF(multi_ret); @@ -2997,12 +3049,60 @@ PyObject *py_ue_ufunction_call(UFunction *u_function, UObject *u_obj, PyObject * return ret; Py_RETURN_NONE; +} + +PyObject* ue_unbind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* py_callable, bool fail_on_wrong_property) +{ + UProperty* u_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); + if (!u_property) + { + if (fail_on_wrong_property) + return PyErr_Format(PyExc_Exception, "unable to find event property %s", TCHAR_TO_UTF8(*event_name)); + Py_RETURN_NONE; } -PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_callable, bool fail_on_wrong_property) + if (auto casted_prop = Cast(u_property)) + { + UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->FindDelegate(u_obj->ue_object, py_callable); + if (py_delegate != nullptr) + { +#if ENGINE_MINOR_VERSION < 23 + FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); +#else + FMulticastScriptDelegate multiscript_delegate = *casted_prop->GetMulticastDelegate(u_obj->ue_object); +#endif + + multiscript_delegate.Remove(py_delegate, FName("PyFakeCallable")); + + // re-assign multicast delegate +#if ENGINE_MINOR_VERSION < 23 + casted_prop->SetPropertyValue_InContainer(u_obj->ue_object, multiscript_delegate); +#else + casted_prop->SetMulticastDelegate(u_obj->ue_object, multiscript_delegate); +#endif + } + } + else if (auto casted_prop_delegate = Cast(u_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 + { + if (fail_on_wrong_property) + return PyErr_Format(PyExc_Exception, "property %s is not an event", TCHAR_TO_UTF8(*event_name)); + } + + Py_RETURN_NONE; +} + +PyObject* ue_bind_pyevent(ue_PyUObject* u_obj, FString event_name, PyObject* py_callable, bool fail_on_wrong_property) { - UProperty *u_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); + UProperty* u_property = u_obj->ue_object->GetClass()->FindPropertyByName(FName(*event_name)); if (!u_property) { if (fail_on_wrong_property) @@ -3012,10 +3112,14 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ if (auto casted_prop = Cast(u_property)) { +#if ENGINE_MINOR_VERSION < 23 FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(u_obj->ue_object); +#else + FMulticastScriptDelegate multiscript_delegate = *casted_prop->GetMulticastDelegate(u_obj->ue_object); +#endif FScriptDelegate script_delegate; - UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); + UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -3023,13 +3127,17 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ multiscript_delegate.Add(script_delegate); // re-assign multicast delegate +#if ENGINE_MINOR_VERSION < 23 casted_prop->SetPropertyValue_InContainer(u_obj->ue_object, multiscript_delegate); +#else + casted_prop->SetMulticastDelegate(u_obj->ue_object, multiscript_delegate); +#endif } else if (auto casted_prop_delegate = Cast(u_property)) { FScriptDelegate script_delegate = casted_prop_delegate->GetPropertyValue_InContainer(u_obj->ue_object); - UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop_delegate->SignatureFunction); + UPythonDelegate* py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(u_obj->ue_object, py_callable, casted_prop_delegate->SignatureFunction); // fake UFUNCTION for bypassing checks script_delegate.BindUFunction(py_delegate, FName("PyFakeCallable")); @@ -3045,10 +3153,10 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_ Py_RETURN_NONE; } -UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_callable, uint32 function_flags) +UFunction* unreal_engine_add_function(UClass* u_class, char* name, PyObject* py_callable, uint32 function_flags) { - UFunction *parent_function = u_class->GetSuperClass()->FindFunctionByName(UTF8_TO_TCHAR(name)); + UFunction* parent_function = u_class->GetSuperClass()->FindFunctionByName(UTF8_TO_TCHAR(name)); // if the function is not available in the parent // check for name collision if (!parent_function) @@ -3057,10 +3165,10 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ { UE_LOG(LogPython, Error, TEXT("function %s is already registered"), UTF8_TO_TCHAR(name)); return nullptr; + } } -} - UPythonFunction *function = NewObject(u_class, UTF8_TO_TCHAR(name), RF_Public | RF_Transient | RF_MarkAsNative); + UPythonFunction* function = NewObject(u_class, UTF8_TO_TCHAR(name), RF_Public | RF_Transient | RF_MarkAsNative); function->SetPyCallable(py_callable); #if ENGINE_MINOR_VERSION < 18 @@ -3078,34 +3186,34 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ // iterate all arguments using inspect.signature() // this is required to maintaining args order - PyObject *inspect = PyImport_ImportModule("inspect"); + PyObject* inspect = PyImport_ImportModule("inspect"); if (!inspect) { return NULL; } - PyObject *signature = PyObject_CallMethod(inspect, (char *)"signature", (char *)"O", py_callable); + PyObject* signature = PyObject_CallMethod(inspect, (char*)"signature", (char*)"O", py_callable); if (!signature) { return NULL; } - PyObject *parameters = PyObject_GetAttrString(signature, "parameters"); + PyObject* parameters = PyObject_GetAttrString(signature, "parameters"); if (!parameters) { return NULL; } - PyObject *annotations = PyObject_GetAttrString(py_callable, "__annotations__"); + PyObject* annotations = PyObject_GetAttrString(py_callable, "__annotations__"); - UField **next_property = &function->Children; - UProperty **next_property_link = &function->PropertyLink; + UField** next_property = &function->Children; + UProperty** next_property_link = &function->PropertyLink; - PyObject *parameters_keys = PyObject_GetIter(parameters); + PyObject* parameters_keys = PyObject_GetIter(parameters); // do not process args if no annotations are available while (annotations) { - PyObject *key = PyIter_Next(parameters_keys); + PyObject* key = PyIter_Next(parameters_keys); if (!key) { if (PyErr_Occurred()) @@ -3115,60 +3223,138 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ if (!PyUnicodeOrString_Check(key)) continue; - const char *p_name = UEPyUnicode_AsUTF8(key); + const char* p_name = UEPyUnicode_AsUTF8(key); - PyObject *value = PyDict_GetItem(annotations, key); + PyObject* value = PyDict_GetItem(annotations, key); if (!value) continue; - UProperty *prop = nullptr; + UProperty* prop = nullptr; if (PyType_Check(value)) { - if ((PyTypeObject *)value == &PyFloat_Type) + if ((PyTypeObject*)value == &PyFloat_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - else if ((PyTypeObject *)value == &PyUnicode_Type) + else if ((PyTypeObject*)value == &PyUnicode_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - else if ((PyTypeObject *)value == &PyBool_Type) + else if ((PyTypeObject*)value == &PyBool_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - else if ((PyTypeObject *)value == &PyLong_Type) + else if ((PyTypeObject*)value == &PyLong_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - } - else if (ue_PyUObject *py_obj = ue_is_pyuobject(value)) - { - if (py_obj->ue_object->IsA()) + else if ((PyTypeObject*)value == &ue_PyFVectorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFVector2DType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFRotatorType) { - UClass *p_u_class = (UClass *)py_obj->ue_object; - if (p_u_class->IsChildOf()) + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFLinearColorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFColorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)value == &ue_PyFTransformType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } +#if ENGINE_MINOR_VERSION > 18 + else if ((PyTypeObject*)value == &ue_PyFQuatType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } +#endif + 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) { - UClassProperty *prop_base = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); - if (p_u_class == UClass::StaticClass()) - { - prop_base->SetMetaClass(UObject::StaticClass()); - } - else - { - prop_base->SetMetaClass(p_u_class->GetClass()); - } - prop_base->PropertyClass = UClass::StaticClass(); - prop = prop_base; + UE_LOG(LogPython, Error, TEXT("missing type info on %s"), UTF8_TO_TCHAR(name)); + return nullptr; } - else + if (PyTuple_Size(type_args) != 1) { - UObjectProperty *prop_base = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); - prop_base->SetPropertyClass(p_u_class); - prop = prop_base; + 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; } + UClassProperty* prop_class = NewObject(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; + UObjectProperty* prop_base = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_base->SetPropertyClass(p_u_class); + prop = prop_base; + } +#if ENGINE_MINOR_VERSION > 17 + else if (py_obj->ue_object->IsA()) + { + UEnumProperty* prop_enum = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + UNumericProperty* prop_underlying = NewObject(prop_enum, TEXT("UnderlyingType"), RF_Public); + prop_enum->SetEnum((UEnum*)py_obj->ue_object); + prop_enum->AddCppProperty(prop_underlying); + prop = prop_enum; + } +#endif + else if (py_obj->ue_object->IsA()) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = (UScriptStruct*)py_obj->ue_object; + prop = prop_struct; } } - // TODO add native types (like vectors, rotators...) + if (prop) { prop->SetPropertyFlags(CPF_Parm); @@ -3187,59 +3373,137 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ // check for return value if (annotations) { - PyObject *py_return_value = PyDict_GetItemString(annotations, "return"); + PyObject* py_return_value = PyDict_GetItemString(annotations, "return"); if (py_return_value) { UE_LOG(LogPython, Warning, TEXT("Return Value found")); - UProperty *prop = nullptr; - char *p_name = (char *) "ReturnValue"; + UProperty* prop = nullptr; + char* p_name = (char*) "ReturnValue"; if (PyType_Check(py_return_value)) { - if ((PyTypeObject *)py_return_value == &PyFloat_Type) + if ((PyTypeObject*)py_return_value == &PyFloat_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - else if ((PyTypeObject *)py_return_value == &PyUnicode_Type) + else if ((PyTypeObject*)py_return_value == &PyUnicode_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - else if ((PyTypeObject *)py_return_value == &PyBool_Type) + else if ((PyTypeObject*)py_return_value == &PyBool_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - else if ((PyTypeObject *)py_return_value == &PyLong_Type) + else if ((PyTypeObject*)py_return_value == &PyLong_Type) { prop = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); } - } - else if (ue_PyUObject *py_obj = ue_is_pyuobject(py_return_value)) - { - if (py_obj->ue_object->IsA()) + else if ((PyTypeObject*)py_return_value == &ue_PyFVectorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFVector2DType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFRotatorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFLinearColorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFColorType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } + else if ((PyTypeObject*)py_return_value == &ue_PyFTransformType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } +#if ENGINE_MINOR_VERSION > 18 + else if ((PyTypeObject*)py_return_value == &ue_PyFQuatType) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = TBaseStructure::Get(); + prop = prop_struct; + } +#endif + else if (PyObject_IsInstance(py_return_value, (PyObject*)& PyType_Type)) { - UClass *p_u_class = (UClass *)py_obj->ue_object; - if (p_u_class->IsChildOf()) + // 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) { - UClassProperty *prop_base = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); - if (p_u_class == UClass::StaticClass()) - { - prop_base->SetMetaClass(UObject::StaticClass()); - } - else - { - prop_base->SetMetaClass(p_u_class->GetClass()); - } - prop_base->PropertyClass = UClass::StaticClass(); - prop = prop_base; + UE_LOG(LogPython, Error, TEXT("missing type info on %s"), UTF8_TO_TCHAR(name)); + return nullptr; } - else + 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()) { - UObjectProperty *prop_base = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); - prop_base->SetPropertyClass(p_u_class); - prop = prop_base; + Py_DECREF(type_args); + UE_LOG(LogPython, Error, TEXT("type for %s must be a UClass"), UTF8_TO_TCHAR(name)); + return nullptr; } + UClassProperty* prop_class = NewObject(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; + UObjectProperty* prop_base = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_base->SetPropertyClass(p_u_class); + prop = prop_base; + } +#if ENGINE_MINOR_VERSION > 17 + else if (py_obj->ue_object->IsA()) + { + UEnumProperty* prop_enum = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + UNumericProperty* prop_underlying = NewObject(prop_enum, TEXT("UnderlyingType"), RF_Public); + prop_enum->SetEnum((UEnum*)py_obj->ue_object); + prop_enum->AddCppProperty(prop_underlying); + prop = prop_enum; + } +#endif + else if (py_obj->ue_object->IsA()) + { + UStructProperty* prop_struct = NewObject(function, UTF8_TO_TCHAR(p_name), RF_Public); + prop_struct->Struct = (UScriptStruct*)py_obj->ue_object; + prop = prop_struct; } } - // TODO add native types (like vectors, rotators...) + if (prop) { prop->SetPropertyFlags(CPF_Parm | CPF_OutParm | CPF_ReturnParm); @@ -3267,11 +3531,11 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ TFieldIterator It(parent_function); while (It) { - UProperty *p = *It; + UProperty* p = *It; 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()); - UClassProperty *ucp = Cast(p); + UClassProperty* ucp = Cast(p); if (ucp) { UE_LOG(LogPython, Warning, TEXT("Parent UClassProperty = %p %s %p %s"), ucp->PropertyClass, *ucp->PropertyClass->GetName(), ucp->MetaClass, *ucp->MetaClass->GetName()); @@ -3283,11 +3547,11 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ TFieldIterator It2(function); while (It2) { - UProperty *p = *It2; + UProperty* p = *It2; 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()); - UClassProperty *ucp = Cast(p); + UClassProperty* ucp = Cast(p); if (ucp) { UE_LOG(LogPython, Warning, TEXT("Function UClassProperty = %p %s %p %s"), ucp->PropertyClass, *ucp->PropertyClass->GetName(), ucp->MetaClass, *ucp->MetaClass->GetName()); @@ -3307,7 +3571,7 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ TFieldIterator props(function, EFieldIteratorFlags::ExcludeSuper); for (; props; ++props) { - UProperty *p = *props; + UProperty* p = *props; if (p->HasAnyPropertyFlags(CPF_Parm)) { function->NumParms++; @@ -3336,9 +3600,9 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ #endif #if ENGINE_MINOR_VERSION > 18 - function->SetNativeFunc((FNativeFuncPtr)&UPythonFunction::CallPythonCallable); + function->SetNativeFunc((FNativeFuncPtr)& UPythonFunction::CallPythonCallable); #else - function->SetNativeFunc((Native)&UPythonFunction::CallPythonCallable); + function->SetNativeFunc((Native)& UPythonFunction::CallPythonCallable); #endif function->Next = u_class->Children; @@ -3379,26 +3643,26 @@ UFunction *unreal_engine_add_function(UClass *u_class, char *name, PyObject *py_ #endif return function; - } +} -FGuid *ue_py_check_fguid(PyObject *py_obj) +FGuid* ue_py_check_fguid(PyObject* py_obj) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + ue_PyUScriptStruct* ue_py_struct = py_ue_is_uscriptstruct(py_obj); if (!ue_py_struct) { return nullptr; } - if (ue_py_struct->u_struct == FindObject(ANY_PACKAGE, UTF8_TO_TCHAR((char *)"Guid"))) + if (ue_py_struct->u_struct == FindObject(ANY_PACKAGE, UTF8_TO_TCHAR((char*)"Guid"))) { return (FGuid*)ue_py_struct->u_struct_ptr; } return nullptr; } -uint8 * do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct) +uint8* do_ue_py_check_struct(PyObject* py_obj, UScriptStruct* chk_u_struct) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + ue_PyUScriptStruct* ue_py_struct = py_ue_is_uscriptstruct(py_obj); if (!ue_py_struct) { return nullptr; @@ -3412,9 +3676,9 @@ uint8 * do_ue_py_check_struct(PyObject *py_obj, UScriptStruct* chk_u_struct) return nullptr; } -bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct) +bool do_ue_py_check_childstruct(PyObject* py_obj, UScriptStruct* parent_u_struct) { - ue_PyUScriptStruct *ue_py_struct = py_ue_is_uscriptstruct(py_obj); + ue_PyUScriptStruct* ue_py_struct = py_ue_is_uscriptstruct(py_obj); if (!ue_py_struct) { return false; @@ -3426,10 +3690,10 @@ bool do_ue_py_check_childstruct(PyObject *py_obj, UScriptStruct* parent_u_struct #if PY_MAJOR_VERSION >= 3 -static PyObject *init_unreal_engine() +static PyObject * init_unreal_engine() { - PyObject *new_unreal_engine_module = PyModule_Create(&unreal_engine_module); + PyObject* new_unreal_engine_module = PyModule_Create(&unreal_engine_module); if (!new_unreal_engine_module) return nullptr; diff --git a/Source/UnrealEnginePython/Private/UEPyModule.h b/Source/UnrealEnginePython/Private/UEPyModule.h index b193e0a10..9d865cb16 100644 --- a/Source/UnrealEnginePython/Private/UEPyModule.h +++ b/Source/UnrealEnginePython/Private/UEPyModule.h @@ -14,6 +14,15 @@ #include "Wrappers/UEPyFColor.h" #include "Wrappers/UEPyFLinearColor.h" +// backward compatibility for UE4.20 TCHAR_TO_WCHAR +#ifndef TCHAR_TO_WCHAR + // SIZEOF_WCHAR_T is provided by pyconfig.h + #if SIZEOF_WCHAR_T == (PLATFORM_TCHAR_IS_4_BYTES ? 4 : 2) + #define TCHAR_TO_WCHAR(str) str + #else + #define TCHAR_TO_WCHAR(str) (wchar_t*)StringCast(static_cast(str)).Get() + #endif +#endif UWorld *ue_get_uworld(ue_PyUObject *); @@ -26,6 +35,7 @@ void ue_bind_events_for_py_class_by_attribute(UObject *, PyObject *); void ue_autobind_events_for_pyclass(ue_PyUObject *, PyObject *); PyObject *ue_bind_pyevent(ue_PyUObject *, FString, PyObject *, bool); +PyObject *ue_unbind_pyevent(ue_PyUObject *, FString, PyObject *, bool); PyObject *py_ue_ufunction_call(UFunction *, UObject *, PyObject *, int, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UEPySubclassing.cpp b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp index 870a62474..548a83935 100644 --- a/Source/UnrealEnginePython/Private/UEPySubclassing.cpp +++ b/Source/UnrealEnginePython/Private/UEPySubclassing.cpp @@ -346,7 +346,12 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) { if (auto casted_prop = Cast(u_property)) { +#if ENGINE_MINOR_VERSION >= 23 + FMulticastScriptDelegate multiscript_delegate = *casted_prop->GetMulticastDelegate(ObjectInitializer.GetObj()); +#else + FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(ObjectInitializer.GetObj()); +#endif FScriptDelegate script_delegate; UPythonDelegate *py_delegate = FUnrealEnginePythonHouseKeeper::Get()->NewDelegate(ObjectInitializer.GetObj(), mc_value, casted_prop->SignatureFunction); @@ -357,7 +362,11 @@ int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *kwds) multiscript_delegate.Add(script_delegate); // re-assign multicast delegate +#if ENGINE_MINOR_VERSION >= 23 + casted_prop->SetMulticastDelegate(ObjectInitializer.GetObj(), multiscript_delegate); +#else casted_prop->SetPropertyValue_InContainer(ObjectInitializer.GetObj(), multiscript_delegate); +#endif } else { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp index 7eafe7b51..2674c0d1b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyAnimSequence.cpp @@ -105,6 +105,7 @@ PyObject *py_ue_anim_extract_root_motion(ue_PyUObject * self, PyObject * args) #if WITH_EDITOR #if ENGINE_MINOR_VERSION > 13 +#if ENGINE_MINOR_VERSION < 23 PyObject *py_ue_anim_sequence_update_compressed_track_map_from_raw(ue_PyUObject * self, PyObject * args) { ue_py_check(self); @@ -117,6 +118,7 @@ PyObject *py_ue_anim_sequence_update_compressed_track_map_from_raw(ue_PyUObject Py_RETURN_NONE; } +#endif PyObject *py_ue_anim_sequence_get_raw_animation_data(ue_PyUObject * self, PyObject * args) diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyFoliage.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyFoliage.cpp index e474fda19..0a9498d2a 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyFoliage.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyFoliage.cpp @@ -37,8 +37,11 @@ PyObject *py_ue_get_foliage_types(ue_PyUObject *self, PyObject * args) PyObject *py_list = PyList_New(0); TArray FoliageTypes; - +#if ENGINE_MINOR_VERSION >=23 + foliage_actor->FoliageInfos.GetKeys(FoliageTypes); +#else foliage_actor->FoliageMeshes.GetKeys(FoliageTypes); +#endif for (UFoliageType *FoliageType : FoliageTypes) { @@ -68,12 +71,20 @@ PyObject *py_ue_get_foliage_instances(ue_PyUObject *self, PyObject * args) if (!foliage_type) return PyErr_Format(PyExc_Exception, "argument is not a UFoliageType"); +#if ENGINE_MINOR_VERSION >= 23 + if (!foliage_actor->FoliageInfos.Contains(foliage_type)) +#else if (!foliage_actor->FoliageMeshes.Contains(foliage_type)) +#endif { return PyErr_Format(PyExc_Exception, "specified UFoliageType not found in AInstancedFoliageActor"); } +#if ENGINE_MINOR_VERSION >= 23 + FFoliageInfo& info = foliage_actor->FoliageInfos[foliage_type].Get(); +#else FFoliageMeshInfo& info = foliage_actor->FoliageMeshes[foliage_type].Get(); +#endif PyObject *py_list = PyList_New(0); @@ -111,7 +122,11 @@ PyObject *py_ue_add_foliage_asset(ue_PyUObject *self, PyObject * args) AInstancedFoliageActor *ifa = AInstancedFoliageActor::GetInstancedFoliageActorForCurrentLevel(world, true); if (u_object->IsA()) { +#if ENGINE_MINOR_VERSION >= 23 + foliage_type = ifa->GetLocalFoliageTypeForSource(u_object); +#else foliage_type = ifa->GetLocalFoliageTypeForMesh((UStaticMesh *)u_object); +#endif if (!foliage_type) { ifa->AddMesh((UStaticMesh *)u_object, &foliage_type); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp index 883a6ba0f..adacab7ec 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyLandscape.cpp @@ -8,35 +8,35 @@ #include "Runtime/Landscape/Classes/LandscapeInfo.h" #include "GameFramework/GameModeBase.h" -PyObject *py_ue_create_landscape_info(ue_PyUObject *self, PyObject * args) +PyObject* py_ue_create_landscape_info(ue_PyUObject* self, PyObject* args) { ue_py_check(self); - ALandscapeProxy *landscape = ue_py_check_type(self); + ALandscapeProxy* landscape = ue_py_check_type(self); if (!landscape) return PyErr_Format(PyExc_Exception, "uobject is not a ULandscapeProxy"); Py_RETURN_UOBJECT(landscape->CreateLandscapeInfo()); } -PyObject *py_ue_get_landscape_info(ue_PyUObject *self, PyObject * args) +PyObject* py_ue_get_landscape_info(ue_PyUObject* self, PyObject* args) { ue_py_check(self); - ALandscapeProxy *landscape = ue_py_check_type(self); + ALandscapeProxy* landscape = ue_py_check_type(self); if (!landscape) return PyErr_Format(PyExc_Exception, "uobject is not a ULandscapeProxy"); - ULandscapeInfo *info = landscape->GetLandscapeInfo(); + ULandscapeInfo* info = landscape->GetLandscapeInfo(); if (!info) Py_RETURN_NONE; Py_RETURN_UOBJECT(info); } -PyObject *py_ue_landscape_import(ue_PyUObject *self, PyObject * args) +PyObject* py_ue_landscape_import(ue_PyUObject* self, PyObject* args) { ue_py_check(self); @@ -51,7 +51,7 @@ PyObject *py_ue_landscape_import(ue_PyUObject *self, PyObject * args) if (!PyArg_ParseTuple(args, "iiiiy*|i:landscape_import", §ion_size, §ions_per_component, &component_x, &component_y, &heightmap_buffer, &layer_type)) return nullptr; - ALandscapeProxy *landscape = ue_py_check_type(self); + ALandscapeProxy* landscape = ue_py_check_type(self); if (!landscape) return PyErr_Format(PyExc_Exception, "uobject is not a ULandscapeProxy"); @@ -62,16 +62,29 @@ PyObject *py_ue_landscape_import(ue_PyUObject *self, PyObject * args) if (heightmap_buffer.len < (Py_ssize_t)(size_x * size_y * sizeof(uint16))) return PyErr_Format(PyExc_Exception, "not enough heightmap data, expecting %lu bytes", size_x * size_y * sizeof(uint16)); - uint16 *data = (uint16 *)heightmap_buffer.buf; + uint16* data = (uint16*)heightmap_buffer.buf; TArray infos; +#if ENGINE_MINOR_VERSION < 23 landscape->Import(FGuid::NewGuid(), 0, 0, size_x - 1, size_y - 1, sections_per_component, section_size, data, nullptr, infos, (ELandscapeImportAlphamapType)layer_type); +#else + TMap> HeightDataPerLayers; + TArray HeightData; + for (uint32 i = 0; i < (heightmap_buffer.len / sizeof(uint16)); i++) + { + HeightData.Add(data[i]); + } + HeightDataPerLayers.Add(FGuid(), HeightData); + TMap> MaterialLayersInfo; + MaterialLayersInfo.Add(FGuid(), infos); + landscape->Import(FGuid::NewGuid(), 0, 0, size_x - 1, size_y - 1, sections_per_component, section_size, HeightDataPerLayers, nullptr, MaterialLayersInfo, (ELandscapeImportAlphamapType)layer_type); +#endif Py_RETURN_NONE; } -PyObject *py_ue_landscape_export_to_raw_mesh(ue_PyUObject *self, PyObject * args) +PyObject* py_ue_landscape_export_to_raw_mesh(ue_PyUObject* self, PyObject* args) { ue_py_check(self); @@ -81,14 +94,18 @@ PyObject *py_ue_landscape_export_to_raw_mesh(ue_PyUObject *self, PyObject * args if (!PyArg_ParseTuple(args, "|i:landscape_import", &lod)) return nullptr; - ALandscapeProxy *landscape = ue_py_check_type(self); + ALandscapeProxy* landscape = ue_py_check_type(self); if (!landscape) return PyErr_Format(PyExc_Exception, "uobject is not a ULandscapeProxy"); +#if ENGINE_MINOR_VERSION > 21 + return PyErr_Format(PyExc_Exception, "MeshDescription struct is still unsupported");; +#else FRawMesh raw_mesh; if (!landscape->ExportToRawMesh(lod, raw_mesh)) return PyErr_Format(PyExc_Exception, "unable to export landscape to FRawMesh"); return py_ue_new_fraw_mesh(raw_mesh); +#endif } #endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp index 90b47da3a..c958094f6 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyMaterial.cpp @@ -505,7 +505,11 @@ PyObject *py_ue_static_mesh_set_collision_for_lod(ue_PyUObject *self, PyObject * 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.bEnableCollision = enabled; mesh->SectionInfoMap.Set(lod_index, material_index, info); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp index 43ef8aeca..3b2983b99 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.cpp @@ -482,6 +482,87 @@ PyObject *py_ue_get_metadata(ue_PyUObject * self, PyObject * args) return PyErr_Format(PyExc_TypeError, "the object does not support MetaData"); } +PyObject *py_ue_get_metadata_tag(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + char *metadata_tag_key; + if (!PyArg_ParseTuple(args, "s:get_metadata_tag", &metadata_tag_key)) + { + return nullptr; + } + + const FString& Value = self->ue_object->GetOutermost()->GetMetaData()->GetValue(self->ue_object, UTF8_TO_TCHAR(metadata_tag_key)); + return PyUnicode_FromString(TCHAR_TO_UTF8(*Value)); +} + +PyObject *py_ue_has_metadata_tag(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + char *metadata_tag_key; + if (!PyArg_ParseTuple(args, "s:has_metadata_tag", &metadata_tag_key)) + { + return nullptr; + } + + if (self->ue_object->GetOutermost()->GetMetaData()->HasValue(self->ue_object, UTF8_TO_TCHAR(metadata_tag_key))) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +PyObject *py_ue_remove_metadata_tag(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + char *metadata_tag_key; + if (!PyArg_ParseTuple(args, "s:remove_metadata_tag", &metadata_tag_key)) + { + return nullptr; + } + + self->ue_object->GetOutermost()->GetMetaData()->RemoveValue(self->ue_object, UTF8_TO_TCHAR(metadata_tag_key)); + Py_RETURN_NONE; +} + +PyObject *py_ue_set_metadata_tag(ue_PyUObject * self, PyObject * args) +{ + + ue_py_check(self); + + char *metadata_tag_key; + char *metadata_tag_value; + if (!PyArg_ParseTuple(args, "ss:set_metadata_tag", &metadata_tag_key, &metadata_tag_value)) + { + return nullptr; + } + + self->ue_object->GetOutermost()->GetMetaData()->SetValue(self->ue_object, UTF8_TO_TCHAR(metadata_tag_key), UTF8_TO_TCHAR(metadata_tag_value)); + Py_RETURN_NONE; +} + + +PyObject *py_ue_metadata_tags(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + TMap *TagsMap = self->ue_object->GetOutermost()->GetMetaData()->GetMapForObject(self->ue_object); + if (!TagsMap) + Py_RETURN_NONE; + + PyObject* py_list = PyList_New(0); + for (TPair< FName, FString>& Pair : *TagsMap) + { + PyList_Append(py_list, PyUnicode_FromString(TCHAR_TO_UTF8(*Pair.Key.ToString()))); + } + return py_list; +} + PyObject *py_ue_has_metadata(ue_PyUObject * self, PyObject * args) { @@ -498,11 +579,9 @@ PyObject *py_ue_has_metadata(ue_PyUObject * self, PyObject * args) UClass *u_class = (UClass *)self->ue_object; if (u_class->HasMetaData(FName(UTF8_TO_TCHAR(metadata_key)))) { - Py_INCREF(Py_True); - return Py_True; + Py_RETURN_TRUE; } - Py_INCREF(Py_False); - return Py_False; + Py_RETURN_FALSE; } if (self->ue_object->IsA()) @@ -1005,7 +1084,11 @@ PyObject *py_ue_broadcast(ue_PyUObject *self, PyObject *args) if (auto casted_prop = Cast(u_property)) { +#if ENGINE_MINOR_VERSION >= 23 + FMulticastScriptDelegate multiscript_delegate = *casted_prop->GetMulticastDelegate(self->ue_object); +#else FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer(self->ue_object); +#endif uint8 *parms = (uint8 *)FMemory_Alloca(casted_prop->SignatureFunction->PropertiesSize); FMemory::Memzero(parms, casted_prop->SignatureFunction->PropertiesSize); @@ -1438,6 +1521,25 @@ PyObject *py_ue_bind_event(ue_PyUObject * self, PyObject * args) return ue_bind_pyevent(self, FString(event_name), py_callable, true); } +PyObject *py_ue_unbind_event(ue_PyUObject * self, PyObject * args) +{ + ue_py_check(self); + + char *event_name; + PyObject *py_callable; + if (!PyArg_ParseTuple(args, "sO:bind_event", &event_name, &py_callable)) + { + return NULL; + } + + if (!PyCallable_Check(py_callable)) + { + return PyErr_Format(PyExc_Exception, "object is not callable"); + } + + return ue_unbind_pyevent(self, FString(event_name), py_callable, true); +} + PyObject *py_ue_delegate_bind_ufunction(ue_PyUObject * self, PyObject * args) { ue_py_check(self); @@ -1850,7 +1952,7 @@ PyObject *py_ue_add_property(ue_PyUObject * self, PyObject * args) // TODO add default value Py_RETURN_UOBJECT(u_property); -} + } PyObject *py_ue_as_dict(ue_PyUObject * self, PyObject * args) { @@ -2037,7 +2139,7 @@ PyObject *py_ue_save_package(ue_PyUObject * self, PyObject * args) package->FileName = *FPackageName::LongPackageNameToFilename(package->GetPathName(), bIsMap ? FPackageName::GetMapPackageExtension() : FPackageName::GetAssetPackageExtension()); } - if (UPackage::SavePackage(package, u_object, RF_NoFlags, *package->FileName.ToString())) + if (UPackage::SavePackage(package, u_object, RF_Standalone, *package->FileName.ToString())) { FAssetRegistryModule::AssetCreated(u_object); Py_RETURN_UOBJECT(u_object); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h index d43422274..6fb4678fe 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyObject.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyObject.h @@ -51,6 +51,7 @@ PyObject *py_ue_enum_user_defined_names(ue_PyUObject *, PyObject *); PyObject *py_ue_bind_event(ue_PyUObject *, PyObject *); +PyObject *py_ue_unbind_event(ue_PyUObject *, PyObject *); PyObject *py_ue_add_function(ue_PyUObject *, PyObject *); PyObject *py_ue_add_property(ue_PyUObject *, PyObject *); @@ -97,6 +98,12 @@ PyObject *py_ue_get_metadata(ue_PyUObject *, PyObject *); PyObject *py_ue_set_metadata(ue_PyUObject *, PyObject *); PyObject *py_ue_has_metadata(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_metadata_tag(ue_PyUObject *, PyObject *); +PyObject *py_ue_set_metadata_tag(ue_PyUObject *, PyObject *); +PyObject *py_ue_has_metadata_tag(ue_PyUObject *, PyObject *); +PyObject *py_ue_remove_metadata_tag(ue_PyUObject *, PyObject *); +PyObject *py_ue_metadata_tags(ue_PyUObject *, PyObject *); + PyObject *py_ue_import_custom_properties(ue_PyUObject *, PyObject *); #endif @@ -105,4 +112,4 @@ PyObject *py_ue_render_thumbnail(ue_PyUObject *, PyObject *); PyObject *py_ue_to_bytes(ue_PyUObject *, PyObject *); PyObject *py_ue_to_bytearray(ue_PyUObject *, PyObject *); -PyObject *py_ue_from_bytes(ue_PyUObject *, PyObject *); \ No newline at end of file +PyObject *py_ue_from_bytes(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp index 32b11dd48..8858a7936 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.cpp @@ -68,6 +68,51 @@ static bool magic_get_frame_number(UMovieScene *MovieScene, PyObject *py_obj, FF } #if WITH_EDITOR +#if ENGINE_MINOR_VERSION > 21 +static void ImportTransformChannel(const FRichCurve& Source, FMovieSceneFloatChannel* Dest, FFrameRate DestFrameRate, bool bNegateTangents) +{ + TMovieSceneChannelData ChannelData = Dest->GetData(); + ChannelData.Reset(); + double DecimalRate = DestFrameRate.AsDecimal(); + for (int32 KeyIndex = 0; KeyIndex < Source.Keys.Num(); ++KeyIndex) + { + float ArriveTangent = Source.Keys[KeyIndex].ArriveTangent; + if (KeyIndex > 0) + { + ArriveTangent = ArriveTangent / ((Source.Keys[KeyIndex].Value - Source.Keys[KeyIndex - 1].Value) * DecimalRate); + } + + float LeaveTangent = Source.Keys[KeyIndex].LeaveTangent; + if (KeyIndex < Source.Keys.Num() - 1) + { + LeaveTangent = LeaveTangent / ((Source.Keys[KeyIndex + 1].Value - Source.Keys[KeyIndex].Value) * DecimalRate); + } + + if (bNegateTangents) + { + ArriveTangent = -ArriveTangent; + LeaveTangent = -LeaveTangent; + } + + FFrameNumber KeyTime = (Source.Keys[KeyIndex].Value * DestFrameRate).RoundToFrame(); + if (ChannelData.FindKey(KeyTime) == INDEX_NONE) + { + FMovieSceneFloatValue NewKey(Source.Keys[KeyIndex].Value); + + NewKey.InterpMode = Source.Keys[KeyIndex].InterpMode; + NewKey.TangentMode = Source.Keys[KeyIndex].TangentMode; + NewKey.Tangent.ArriveTangent = ArriveTangent / DestFrameRate.AsDecimal(); + NewKey.Tangent.LeaveTangent = LeaveTangent / DestFrameRate.AsDecimal(); + NewKey.Tangent.TangentWeightMode = RCTWM_WeightedNone; + NewKey.Tangent.ArriveTangentWeight = 0.0f; + NewKey.Tangent.LeaveTangentWeight = 0.0f; + ChannelData.AddKey(KeyTime, NewKey); + } + } + + Dest->AutoSetTangents(); +} +#else static void ImportTransformChannel(const FInterpCurveFloat& Source, FMovieSceneFloatChannel* Dest, FFrameRate DestFrameRate, bool bNegateTangents) { TMovieSceneChannelData ChannelData = Dest->GetData(); @@ -94,23 +139,36 @@ static void ImportTransformChannel(const FInterpCurveFloat& Source, FMovieSceneF } FFrameNumber KeyTime = (Source.Points[KeyIndex].InVal * DestFrameRate).RoundToFrame(); +#if ENGINE_MINOR_VERSION > 20 + FMatineeImportTools::SetOrAddKey(ChannelData, KeyTime, Source.Points[KeyIndex].OutVal, ArriveTangent, LeaveTangent, Source.Points[KeyIndex].InterpMode, DestFrameRate); +#else FMatineeImportTools::SetOrAddKey(ChannelData, KeyTime, Source.Points[KeyIndex].OutVal, ArriveTangent, LeaveTangent, Source.Points[KeyIndex].InterpMode); +#endif } Dest->AutoSetTangents(); } +#endif static bool ImportFBXTransform(FString NodeName, UMovieScene3DTransformSection* TransformSection, UnFbx::FFbxCurvesAPI& CurveAPI) { // Look for transforms explicitly + FTransform DefaultTransform; + +#if ENGINE_MINOR_VERSION > 21 + FRichCurve Translation[3]; + FRichCurve EulerRotation[3]; + FRichCurve Scale[3]; +#else FInterpCurveFloat Translation[3]; FInterpCurveFloat EulerRotation[3]; FInterpCurveFloat Scale[3]; - FTransform DefaultTransform; +#endif CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform); + TransformSection->Modify(); FFrameRate FrameRate = TransformSection->GetTypedOuter()->GetTickResolution(); @@ -911,6 +969,71 @@ PyObject *py_ue_sequencer_set_playback_range(ue_PyUObject *self, PyObject * args Py_RETURN_NONE; } +PyObject *py_ue_sequencer_get_playback_range(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + ULevelSequence *seq = ue_py_check_type(self); + if (!seq) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + UMovieScene *scene = seq->GetMovieScene(); + +#if ENGINE_MINOR_VERSION < 20 + TRange range = scene->GetPlaybackRange(); + return Py_BuildValue("(ff)", range.GetLowerBoundValue(), range.GetUpperBoundValue()); +#else + TRange range = scene->GetPlaybackRange(); + + return Py_BuildValue("(OO)", py_ue_new_fframe_number(range.GetLowerBoundValue()), py_ue_new_fframe_number(range.GetUpperBoundValue())); + +#endif +} + +PyObject *py_ue_sequencer_set_working_range(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + ULevelSequence *seq = ue_py_check_type(self); + if (!seq) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + UMovieScene *scene = seq->GetMovieScene(); + + float start_time; + float end_time; + if (!PyArg_ParseTuple(args, "ff:sequencer_set_working_range", &start_time, &end_time)) + { + return nullptr; + } + + scene->SetWorkingRange(start_time, end_time); + + Py_RETURN_NONE; +} + +PyObject *py_ue_sequencer_set_view_range(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + ULevelSequence *seq = ue_py_check_type(self); + if (!seq) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + UMovieScene *scene = seq->GetMovieScene(); + + float start_time; + float end_time; + if (!PyArg_ParseTuple(args, "ff:sequencer_set_view_range", &start_time, &end_time)) + { + return nullptr; + } + + scene->SetViewRange(start_time, end_time); + + Py_RETURN_NONE; +} + PyObject *py_ue_sequencer_set_section_range(ue_PyUObject *self, PyObject * args) { @@ -965,6 +1088,27 @@ PyObject *py_ue_sequencer_set_section_range(ue_PyUObject *self, PyObject * args) Py_RETURN_NONE; } +PyObject *py_ue_sequencer_get_selection_range(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + ULevelSequence *seq = ue_py_check_type(self); + if (!seq) + return PyErr_Format(PyExc_Exception, "uobject is not a LevelSequence"); + UMovieScene *scene = seq->GetMovieScene(); + +#if ENGINE_MINOR_VERSION < 20 + TRange range = scene->GetSelectionRange(); + return Py_BuildValue("(ff)", range.GetLowerBoundValue(), range.GetUpperBoundValue()); +#else + TRange range = scene->GetSelectionRange(); + + return Py_BuildValue("(OO)", py_ue_new_fframe_number(range.GetLowerBoundValue()), py_ue_new_fframe_number(range.GetUpperBoundValue())); + +#endif +} + PyObject *py_ue_sequencer_section_add_key(ue_PyUObject *self, PyObject * args) { @@ -1504,9 +1648,15 @@ PyObject *py_ue_sequencer_import_fbx_transform(ue_PyUObject *self, PyObject * ar continue; // Look for transforms explicitly +#if ENGINE_MINOR_VERSION > 21 + FRichCurve Translation[3]; + FRichCurve EulerRotation[3]; + FRichCurve Scale[3]; +#else FInterpCurveFloat Translation[3]; FInterpCurveFloat EulerRotation[3]; FInterpCurveFloat Scale[3]; +#endif FTransform DefaultTransform; #if ENGINE_MINOR_VERSION >= 18 CurveAPI.GetConvertedTransformCurveData(NodeName, Translation[0], Translation[1], Translation[2], EulerRotation[0], EulerRotation[1], EulerRotation[2], Scale[0], Scale[1], Scale[2], DefaultTransform); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h index ac22a41a7..83185d612 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPySequencer.h @@ -10,7 +10,11 @@ PyObject *py_ue_sequencer_track_sections(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_get_camera_cut_track(ue_PyUObject *, PyObject *); #if WITH_EDITOR PyObject *py_ue_sequencer_set_playback_range(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_set_view_range(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_set_working_range(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_set_section_range(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_get_playback_range(ue_PyUObject *, PyObject *); +PyObject *py_ue_sequencer_get_selection_range(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_folders(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_create_folder(ue_PyUObject *, PyObject *); PyObject *py_ue_sequencer_set_display_name(ue_PyUObject *, PyObject *); diff --git a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp index 86747cbfa..3386d4537 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPySkeletal.cpp @@ -985,7 +985,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO points_to_map.Add(vertex_index); +#if ENGINE_MINOR_VERSION > 20 SkeletalMeshImportData::FMeshWedge wedge; +#else + FMeshWedge wedge; +#endif wedge.iVertex = vertex_index; wedge.Color = ss_vertex->ss_vertex.Color; for (int32 i = 0; i < MAX_TEXCOORDS; i++) @@ -996,7 +1000,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO for (int32 i = 0; i < MAX_TOTAL_INFLUENCES; i++) { +#if ENGINE_MINOR_VERSION > 20 SkeletalMeshImportData::FVertInfluence influence; +#else + FVertInfluence influence; +#endif influence.VertIndex = wedge_index; influence.BoneIndex = ss_vertex->ss_vertex.InfluenceBones[i]; influence.Weight = ss_vertex->ss_vertex.InfluenceWeights[i] / 255.f; @@ -1018,7 +1026,11 @@ PyObject *py_ue_skeletal_mesh_build_lod(ue_PyUObject *self, PyObject * args, PyO for (int32 i = 0; i < wedges.Num(); i += 3) { +#if ENGINE_MINOR_VERSION > 20 SkeletalMeshImportData::FMeshFace face; +#else + FMeshFace face; +#endif face.iWedge[0] = i; face.iWedge[1] = i + 1; face.iWedge[2] = i + 2; diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp index deb05477e..eaf1a1cf8 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.cpp @@ -1,11 +1,27 @@ #include "UEPyStaticMesh.h" +#include "Engine/StaticMesh.h" +PyObject *py_ue_static_mesh_get_bounds(ue_PyUObject *self, PyObject * args) +{ + ue_py_check(self); + UStaticMesh *mesh = ue_py_check_type(self); + if (!mesh) + return PyErr_Format(PyExc_Exception, "uobject is not a UStaticMesh"); + + FBoxSphereBounds bounds = mesh->GetBounds(); + UScriptStruct *u_struct = FindObject(ANY_PACKAGE, UTF8_TO_TCHAR("BoxSphereBounds")); + if (!u_struct) + { + return PyErr_Format(PyExc_Exception, "unable to get BoxSphereBounds struct"); + } + return py_ue_new_owned_uscriptstruct(u_struct, (uint8 *)&bounds); +} #if WITH_EDITOR -#include "Engine/StaticMesh.h" #include "Wrappers/UEPyFRawMesh.h" #include "Editor/UnrealEd/Private/GeomFitUtils.h" +#include "FbxMeshUtils.h" static PyObject *generate_kdop(ue_PyUObject *self, const FVector *directions, uint32 num_directions) { @@ -116,4 +132,24 @@ PyObject *py_ue_static_mesh_get_raw_mesh(ue_PyUObject *self, PyObject * args) return py_ue_new_fraw_mesh(raw_mesh); } -#endif \ No newline at end of file +PyObject *py_ue_static_mesh_import_lod(ue_PyUObject *self, PyObject * args) +{ + ue_py_check(self); + + char *filename; + int lod_level; + if (!PyArg_ParseTuple(args, "si:static_mesh_import_lod", &filename, &lod_level)) + return nullptr; + + UStaticMesh *mesh = ue_py_check_type(self); + if (!mesh) + return PyErr_Format(PyExc_Exception, "uobject is not a UStaticMesh"); + + if (FbxMeshUtils::ImportStaticMeshLOD(mesh, FString(UTF8_TO_TCHAR(filename)), lod_level)) + { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +#endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.h b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.h index 219d68761..46a8c76be 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyStaticMesh.h @@ -4,6 +4,8 @@ #include "UEPyModule.h" +PyObject *py_ue_static_mesh_get_bounds(ue_PyUObject *self, PyObject * args); + #if WITH_EDITOR PyObject *py_ue_static_mesh_build(ue_PyUObject *, PyObject *); PyObject *py_ue_static_mesh_create_body_setup(ue_PyUObject *, PyObject *); @@ -14,4 +16,5 @@ PyObject *py_ue_static_mesh_generate_kdop10y(ue_PyUObject *, PyObject *); PyObject *py_ue_static_mesh_generate_kdop10z(ue_PyUObject *, PyObject *); PyObject *py_ue_static_mesh_generate_kdop18(ue_PyUObject *, PyObject *); PyObject *py_ue_static_mesh_generate_kdop26(ue_PyUObject *, PyObject *); -#endif \ No newline at end of file +PyObject *py_ue_static_mesh_import_lod(ue_PyUObject *, PyObject *); +#endif diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp index 861f10ba6..19a31299b 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.cpp @@ -316,13 +316,30 @@ PyObject *py_ue_set_current_level(ue_PyUObject *self, PyObject * args) if (!level) return PyErr_Format(PyExc_Exception, "argument is not a ULevel"); +#if WITH_EDITOR || ENGINE_MINOR_VERSION < 22 + if (world->SetCurrentLevel(level)) Py_RETURN_TRUE; +#endif Py_RETURN_FALSE; } #if WITH_EDITOR +PyObject *py_ue_get_level_script_blueprint(ue_PyUObject *self, PyObject * args) +{ + + ue_py_check(self); + + ULevel *level = ue_py_check_type(self); + if (!level) + { + return PyErr_Format(PyExc_Exception, "uobject is not a ULevel"); + } + + Py_RETURN_UOBJECT((UObject*)level->GetLevelScriptBlueprint()); +} + PyObject *py_ue_world_create_folder(ue_PyUObject *self, PyObject * args) { diff --git a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.h b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.h index af2c40665..16f07ae7e 100644 --- a/Source/UnrealEnginePython/Private/UObject/UEPyWorld.h +++ b/Source/UnrealEnginePython/Private/UObject/UEPyWorld.h @@ -35,5 +35,6 @@ PyObject *py_ue_world_create_folder(ue_PyUObject *, PyObject *); PyObject *py_ue_world_delete_folder(ue_PyUObject *, PyObject *); PyObject *py_ue_world_rename_folder(ue_PyUObject *, PyObject *); PyObject *py_ue_world_folders(ue_PyUObject *, PyObject *); +PyObject *py_ue_get_level_script_blueprint(ue_PyUObject *, PyObject *); #endif diff --git a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp index bb79b8c8f..211124849 100644 --- a/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp +++ b/Source/UnrealEnginePython/Private/UnrealEnginePython.cpp @@ -34,9 +34,14 @@ const char *ue4_module_options = "linux_global_symbols"; #include "Runtime/Core/Public/Misc/CommandLine.h" #include "Runtime/Core/Public/Misc/ConfigCacheIni.h" #include "Runtime/Core/Public/GenericPlatform/GenericPlatformFile.h" +#include "Runtime/Core/Public/GenericPlatform/GenericPlatformMisc.h" #include "Runtime/Core/Public/HAL/FileManagerGeneric.h" +#if PLATFORM_WINDOWS +#include +#endif + #if PLATFORM_ANDROID #include "Android/AndroidJNI.h" #include "Android/AndroidApplication.h" @@ -116,7 +121,11 @@ void FUnrealEnginePythonModule::UESetupPythonInterpreter(bool verbose) for (int32 i = 0; i < Args.Num(); i++) { #if PY_MAJOR_VERSION >= 3 + #if ENGINE_MINOR_VERSION >= 20 + argv[i] = (wchar_t *)(TCHAR_TO_WCHAR(*Args[i])); + #else argv[i] = (wchar_t *)(*Args[i]); + #endif #else argv[i] = TCHAR_TO_UTF8(*Args[i]); #endif @@ -362,7 +371,11 @@ void FUnrealEnginePythonModule::StartupModule() const int32 MaxPathVarLen = 32768; FString OrigPathVar = FString::ChrN(MaxPathVarLen, TEXT('\0')); +#if ENGINE_MINOR_VERSION >= 21 + OrigPathVar = FPlatformMisc::GetEnvironmentVariable(TEXT("PATH")); +#else FPlatformMisc::GetEnvironmentVariable(TEXT("PATH"), OrigPathVar.GetCharArray().GetData(), MaxPathVarLen); +#endif // Get the current path and remove elements with python in them, we don't want any conflicts const TCHAR* PathDelimiter = FPlatformMisc::GetPathVarDelimiter(); @@ -447,6 +460,22 @@ void FUnrealEnginePythonModule::StartupModule() Py_Initialize(); +#if PLATFORM_WINDOWS + // Restore stdio state after Py_Initialize set it to O_BINARY, otherwise + // everything that the engine will output is going to be encoded in UTF-16. + // The behaviour is described here: https://bugs.python.org/issue16587 + _setmode(_fileno(stdin), O_TEXT); + _setmode(_fileno(stdout), O_TEXT); + _setmode(_fileno(stderr), O_TEXT); + + // Also restore the user-requested UTF-8 flag if relevant (behaviour copied + // from LaunchEngineLoop.cpp). + if (FParse::Param(FCommandLine::Get(), TEXT("UTF8Output"))) + { + FPlatformMisc::SetUTF8Output(); + } +#endif + PyEval_InitThreads(); #if WITH_EDITOR diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp index 4352312ba..7ffa2d05e 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyESlateEnums.cpp @@ -80,7 +80,11 @@ void ue_python_init_eslate_enums(PyObject *ue_module) }; #if ENGINE_MINOR_VERSION > 15 +#if ENGINE_MINOR_VERSION >= 23 +#define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)EnumType::EnumVal) +#else #define ADD_NATIVE_ENUM(EnumType, EnumVal) add_native_enum(#EnumType "." #EnumVal, (uint8)EnumType::Type::EnumVal) +#endif ADD_NATIVE_ENUM(EUserInterfaceActionType, None); ADD_NATIVE_ENUM(EUserInterfaceActionType, Button); ADD_NATIVE_ENUM(EUserInterfaceActionType, ToggleButton); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.cpp index d71188d97..0105dd254 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.cpp @@ -103,7 +103,7 @@ static PyObject *ue_PyFColor_str(ue_PyFColor *self) self->color.R, self->color.G, self->color.B, self->color.A); } -static PyTypeObject ue_PyFColorType = { +PyTypeObject ue_PyFColorType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.FColor", /* tp_name */ sizeof(ue_PyFColor), /* tp_basicsize */ diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.h index ef75b1832..d3a1013e7 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFColor.h @@ -11,6 +11,8 @@ typedef struct FColor color; } ue_PyFColor; +extern PyTypeObject ue_PyFColorType; + PyObject *py_ue_new_fcolor(FColor); ue_PyFColor *py_ue_is_fcolor(PyObject *); @@ -18,4 +20,4 @@ void ue_python_init_fcolor(PyObject *); bool py_ue_color_arg(PyObject *, FColor &); -bool py_ue_get_fcolor(PyObject *, FColor &); \ No newline at end of file +bool py_ue_get_fcolor(PyObject *, FColor &); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.cpp index 2bc9e7b0c..2ed7ffa2d 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.cpp @@ -6,6 +6,8 @@ #include "Components/ActorComponent.h" #include "Runtime/Foliage/Public/InstancedFoliageActor.h" +#if WITH_EDITOR + #define get_instance(x) FFoliageInstance *instance = get_foliage_instance(x);\ if (!instance)\ return nullptr; @@ -14,7 +16,9 @@ if (!instance)\ return -1; -static FFoliageInstance* get_foliage_instance(ue_PyFFoliageInstance *self) + + +static FFoliageInstance* get_foliage_instance(ue_PyFFoliageInstance* self) { if (!self->foliage_actor.IsValid()) { @@ -28,7 +32,12 @@ static FFoliageInstance* get_foliage_instance(ue_PyFFoliageInstance *self) return nullptr; } +#if ENGINE_MINOR_VERSION >= 23 + FFoliageInfo& info = self->foliage_actor->FoliageInfos[self->foliage_type.Get()].Get(); +#else + FFoliageMeshInfo& info = self->foliage_actor->FoliageMeshes[self->foliage_type.Get()].Get(); +#endif if (self->instance_id >= 0 && self->instance_id < info.Instances.Num()) { @@ -39,29 +48,34 @@ static FFoliageInstance* get_foliage_instance(ue_PyFFoliageInstance *self) return nullptr; } -static PyObject *ue_PyFFoliageInstance_str(ue_PyFFoliageInstance *self) +static PyObject* ue_PyFFoliageInstance_str(ue_PyFFoliageInstance* self) { return PyUnicode_FromFormat("", self->instance_id); } -static PyObject *py_ue_ffoliage_instance_get_location(ue_PyFFoliageInstance *self, void *closure) + +static PyObject* py_ue_ffoliage_instance_get_location(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return py_ue_new_fvector(instance->Location); } -static int py_ue_ffoliage_instance_set_location(ue_PyFFoliageInstance *self, PyObject *value, void *closure) +static int py_ue_ffoliage_instance_set_location(ue_PyFFoliageInstance* self, PyObject* value, void* closure) { set_instance(self); if (value) { - ue_PyFVector *vec = py_ue_is_fvector(value); + ue_PyFVector* vec = py_ue_is_fvector(value); if (vec) { TArray instances; instances.Add(self->instance_id); +#if ENGINE_MINOR_VERSION >= 23 + FFoliageInfo& info = self->foliage_actor->FoliageInfos[self->foliage_type.Get()].Get(); +#else FFoliageMeshInfo& info = self->foliage_actor->FoliageMeshes[self->foliage_type.Get()].Get(); +#endif info.PreMoveInstances(self->foliage_actor.Get(), instances); instance->Location = vec->vec; info.PostMoveInstances(self->foliage_actor.Get(), instances); @@ -72,17 +86,21 @@ static int py_ue_ffoliage_instance_set_location(ue_PyFFoliageInstance *self, PyO return -1; } -static int py_ue_ffoliage_instance_set_rotation(ue_PyFFoliageInstance *self, PyObject *value, void *closure) +static int py_ue_ffoliage_instance_set_rotation(ue_PyFFoliageInstance* self, PyObject* value, void* closure) { set_instance(self); if (value) { - ue_PyFRotator *rot = py_ue_is_frotator(value); + ue_PyFRotator* rot = py_ue_is_frotator(value); if (rot) { TArray instances; instances.Add(self->instance_id); +#if ENGINE_MINOR_VERSION >= 23 + FFoliageInfo& info = self->foliage_actor->FoliageInfos[self->foliage_type.Get()].Get(); +#else FFoliageMeshInfo& info = self->foliage_actor->FoliageMeshes[self->foliage_type.Get()].Get(); +#endif info.PreMoveInstances(self->foliage_actor.Get(), instances); instance->Rotation = rot->rot; info.PostMoveInstances(self->foliage_actor.Get(), instances); @@ -93,94 +111,98 @@ static int py_ue_ffoliage_instance_set_rotation(ue_PyFFoliageInstance *self, PyO return -1; } -static PyObject *py_ue_ffoliage_instance_get_draw_scale3d(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_draw_scale3d(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return py_ue_new_fvector(instance->DrawScale3D); } -static PyObject *py_ue_ffoliage_instance_get_flags(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_flags(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return PyLong_FromUnsignedLong(instance->Flags); } -static PyObject *py_ue_ffoliage_instance_get_pre_align_rotation(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_pre_align_rotation(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return py_ue_new_frotator(instance->PreAlignRotation); } -static PyObject *py_ue_ffoliage_instance_get_rotation(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_rotation(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return py_ue_new_frotator(instance->Rotation); } -static PyObject *py_ue_ffoliage_instance_get_zoffset(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_zoffset(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return PyFloat_FromDouble(instance->ZOffset); } -static PyObject *py_ue_ffoliage_instance_get_procedural_guid(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_procedural_guid(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); FGuid guid = instance->ProceduralGuid; - return py_ue_new_owned_uscriptstruct(FindObject(ANY_PACKAGE, UTF8_TO_TCHAR((char *)"Guid")), (uint8 *)&guid); + return py_ue_new_owned_uscriptstruct(FindObject(ANY_PACKAGE, UTF8_TO_TCHAR((char*)"Guid")), (uint8*)& guid); } -static PyObject *py_ue_ffoliage_instance_get_base_id(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_base_id(ue_PyFFoliageInstance* self, void* closure) { get_instance(self); return PyLong_FromLong(instance->BaseId); } -static PyObject *py_ue_ffoliage_instance_get_instance_id(ue_PyFFoliageInstance *self, void *closure) +static PyObject* py_ue_ffoliage_instance_get_instance_id(ue_PyFFoliageInstance* self, void* closure) { return PyLong_FromLong(self->instance_id); } - -static PyObject *py_ue_ffoliage_instance_get_base_component(ue_PyFFoliageInstance *self, void *closure) +#if ENGINE_MINOR_VERSION > 19 +static PyObject * py_ue_ffoliage_instance_get_base_component(ue_PyFFoliageInstance * self, void* closure) { get_instance(self); Py_RETURN_UOBJECT(instance->BaseComponent); } +#endif static PyGetSetDef ue_PyFFoliageInstance_getseters[] = { - { (char *)"location", (getter)py_ue_ffoliage_instance_get_location, (setter)py_ue_ffoliage_instance_set_location, (char *)"", NULL }, - { (char *)"draw_scale3d", (getter)py_ue_ffoliage_instance_get_draw_scale3d, nullptr, (char *)"", NULL }, - { (char *)"flags", (getter)py_ue_ffoliage_instance_get_flags, nullptr, (char *)"", NULL }, - { (char *)"pre_align_rotation", (getter)py_ue_ffoliage_instance_get_pre_align_rotation, nullptr, (char *)"", NULL }, - { (char *)"rotation", (getter)py_ue_ffoliage_instance_get_rotation, (setter)py_ue_ffoliage_instance_set_rotation, (char *)"", NULL }, - { (char *)"zoffset", (getter)py_ue_ffoliage_instance_get_zoffset, nullptr, (char *)"", NULL }, - { (char *)"procedural_guid", (getter)py_ue_ffoliage_instance_get_procedural_guid, nullptr, (char *)"", NULL }, - { (char *)"guid", (getter)py_ue_ffoliage_instance_get_procedural_guid, nullptr, (char *)"", NULL }, - { (char *)"base_id", (getter)py_ue_ffoliage_instance_get_base_id, nullptr, (char *)"", NULL }, - { (char *)"instance_id", (getter)py_ue_ffoliage_instance_get_instance_id, nullptr, (char *)"", NULL }, + { (char*)"location", (getter)py_ue_ffoliage_instance_get_location, (setter)py_ue_ffoliage_instance_set_location, (char*)"", NULL }, + { (char*)"draw_scale3d", (getter)py_ue_ffoliage_instance_get_draw_scale3d, nullptr, (char*)"", NULL }, + { (char*)"flags", (getter)py_ue_ffoliage_instance_get_flags, nullptr, (char*)"", NULL }, + { (char*)"pre_align_rotation", (getter)py_ue_ffoliage_instance_get_pre_align_rotation, nullptr, (char*)"", NULL }, + { (char*)"rotation", (getter)py_ue_ffoliage_instance_get_rotation, (setter)py_ue_ffoliage_instance_set_rotation, (char*)"", NULL }, + { (char*)"zoffset", (getter)py_ue_ffoliage_instance_get_zoffset, nullptr, (char*)"", NULL }, + { (char*)"procedural_guid", (getter)py_ue_ffoliage_instance_get_procedural_guid, nullptr, (char*)"", NULL }, + { (char*)"guid", (getter)py_ue_ffoliage_instance_get_procedural_guid, nullptr, (char*)"", NULL }, + { (char*)"base_id", (getter)py_ue_ffoliage_instance_get_base_id, nullptr, (char*)"", NULL }, + { (char*)"instance_id", (getter)py_ue_ffoliage_instance_get_instance_id, nullptr, (char*)"", NULL }, +#if ENGINE_MINOR_VERSION > 19 + { (char*)"base_component", (getter)py_ue_ffoliage_instance_get_base_component, nullptr, (char*)"", NULL }, +#endif { NULL } /* Sentinel */ }; -static PyObject *py_ue_ffoliage_instance_get_instance_world_transform(ue_PyFFoliageInstance *self, PyObject * args) +static PyObject* py_ue_ffoliage_instance_get_instance_world_transform(ue_PyFFoliageInstance* self, PyObject* args) { get_instance(self); return py_ue_new_ftransform(instance->GetInstanceWorldTransform()); } -static PyObject *py_ue_ffoliage_instance_align_to_normal(ue_PyFFoliageInstance *self, PyObject * args) +static PyObject* py_ue_ffoliage_instance_align_to_normal(ue_PyFFoliageInstance* self, PyObject* args) { get_instance(self); - PyObject *py_vec; + PyObject* py_vec; float align_max_angle = 0; if (!PyArg_ParseTuple(args, "O|f:align_to_normal", &py_vec, &align_max_angle)) return nullptr; - ue_PyFVector *vec = py_ue_is_fvector(py_vec); + ue_PyFVector* vec = py_ue_is_fvector(py_vec); if (!vec) { return PyErr_Format(PyExc_Exception, "argument is not an FVector"); @@ -235,7 +257,7 @@ static PyTypeObject ue_PyFFoliageInstanceType = { ue_PyFFoliageInstance_getseters, }; -void ue_python_init_ffoliage_instance(PyObject *ue_module) +void ue_python_init_ffoliage_instance(PyObject* ue_module) { ue_PyFFoliageInstanceType.tp_new = PyType_GenericNew; @@ -243,14 +265,16 @@ void ue_python_init_ffoliage_instance(PyObject *ue_module) return; Py_INCREF(&ue_PyFFoliageInstanceType); - PyModule_AddObject(ue_module, "FoliageInstance", (PyObject *)&ue_PyFFoliageInstanceType); + PyModule_AddObject(ue_module, "FoliageInstance", (PyObject*)& ue_PyFFoliageInstanceType); } -PyObject *py_ue_new_ffoliage_instance(AInstancedFoliageActor *foliage_actor, UFoliageType *foliage_type, int32 instance_id) +PyObject* py_ue_new_ffoliage_instance(AInstancedFoliageActor* foliage_actor, UFoliageType* foliage_type, int32 instance_id) { - ue_PyFFoliageInstance *ret = (ue_PyFFoliageInstance *)PyObject_New(ue_PyFFoliageInstance, &ue_PyFFoliageInstanceType); + ue_PyFFoliageInstance* ret = (ue_PyFFoliageInstance*)PyObject_New(ue_PyFFoliageInstance, &ue_PyFFoliageInstanceType); ret->foliage_actor = TWeakObjectPtr(foliage_actor); ret->foliage_type = TWeakObjectPtr(foliage_type); ret->instance_id = instance_id; - return (PyObject *)ret; -} \ No newline at end of file + return (PyObject*)ret; +} + +#endif \ No newline at end of file diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.h index 409b37fad..ab65fc3c8 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFFoliageInstance.h @@ -1,6 +1,7 @@ #pragma once #include "UnrealEnginePython.h" +#if WITH_EDITOR #include "InstancedFoliage.h" typedef struct @@ -14,3 +15,4 @@ typedef struct void ue_python_init_ffoliage_instance(PyObject *); PyObject *py_ue_new_ffoliage_instance(AInstancedFoliageActor *foliage_actor, UFoliageType *foliage_type, int32 instance_id); +#endif diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFFrameNumber.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFFrameNumber.cpp index bb939c680..b2dc0a0df 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFFrameNumber.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFFrameNumber.cpp @@ -10,7 +10,7 @@ static PyObject *ue_PyFFrameNumber_str(ue_PyFFrameNumber *self) static PyTypeObject ue_PyFFrameNumberType = { PyVarObject_HEAD_INIT(NULL, 0) - "unreal_engine.FMFrameNumber", /* tp_name */ + "unreal_engine.FFrameNumber", /* tp_name */ sizeof(ue_PyFFrameNumber), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ @@ -80,4 +80,4 @@ PyObject *py_ue_new_fframe_number(FFrameNumber frame_number) return (PyObject *)ret; } -#endif \ No newline at end of file +#endif diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp index 056411506..5ca0eb83a 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.cpp @@ -106,7 +106,7 @@ static PyObject *ue_PyFLinearColor_str(ue_PyFLinearColor *self) PyFloat_FromDouble(self->color.A)); } -static PyTypeObject ue_PyFLinearColorType = { +PyTypeObject ue_PyFLinearColorType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.FLinearColor", /* tp_name */ sizeof(ue_PyFLinearColor), /* tp_basicsize */ diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.h index 986ecd2aa..2ade8c420 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFLinearColor.h @@ -11,6 +11,8 @@ typedef struct FLinearColor color; } ue_PyFLinearColor; +extern PyTypeObject ue_PyFLinearColorType; + PyObject *py_ue_new_flinearcolor(FLinearColor); ue_PyFLinearColor *py_ue_is_flinearcolor(PyObject *); @@ -18,4 +20,4 @@ void ue_python_init_flinearcolor(PyObject *); bool py_ue_linearcolor_arg(PyObject *, FLinearColor &); -bool py_ue_get_flinearcolor(PyObject *, FLinearColor &); \ No newline at end of file +bool py_ue_get_flinearcolor(PyObject *, FLinearColor &); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.cpp index 74d914848..41faf7383 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.cpp @@ -152,7 +152,7 @@ static PyObject *ue_PyFQuat_str(ue_PyFQuat *self) PyFloat_FromDouble(self->quat.X), PyFloat_FromDouble(self->quat.Y), PyFloat_FromDouble(self->quat.Z), PyFloat_FromDouble(self->quat.W)); } -static PyTypeObject ue_PyFQuatType = { +PyTypeObject ue_PyFQuatType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.FQuat", /* tp_name */ sizeof(ue_PyFQuat), /* tp_basicsize */ diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.h index 1a39ce0a8..94bd2cf9e 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFQuat.h @@ -11,9 +11,11 @@ typedef struct FQuat quat; } ue_PyFQuat; +extern PyTypeObject ue_PyFQuatType; + PyObject *py_ue_new_fquat(FQuat); ue_PyFQuat *py_ue_is_fquat(PyObject *); void ue_python_init_fquat(PyObject *); -bool py_ue_quat_arg(PyObject *, FQuat &); \ No newline at end of file +bool py_ue_quat_arg(PyObject *, FQuat &); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp index edcc35348..3558faa5b 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.cpp @@ -93,7 +93,7 @@ static PyObject *ue_PyFRotator_str(ue_PyFRotator *self) PyFloat_FromDouble(self->rot.Roll), PyFloat_FromDouble(self->rot.Pitch), PyFloat_FromDouble(self->rot.Yaw)); } -static PyTypeObject ue_PyFRotatorType = { +PyTypeObject ue_PyFRotatorType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.FRotator", /* tp_name */ sizeof(ue_PyFRotator), /* tp_basicsize */ diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.h index a4ea71a84..2d3f6df4c 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFRotator.h @@ -10,9 +10,11 @@ typedef struct { FRotator rot; } ue_PyFRotator; +extern PyTypeObject ue_PyFRotatorType; + PyObject *py_ue_new_frotator(FRotator); ue_PyFRotator *py_ue_is_frotator(PyObject *); void ue_python_init_frotator(PyObject *); -bool py_ue_rotator_arg(PyObject *, FRotator &); \ No newline at end of file +bool py_ue_rotator_arg(PyObject *, FRotator &); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp index 11548407a..b3a6b654a 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.cpp @@ -220,7 +220,7 @@ static PyObject *ue_PyFTransform_str(ue_PyFTransform *self) } -static PyTypeObject ue_PyFTransformType = { +PyTypeObject ue_PyFTransformType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.FTransform", /* tp_name */ sizeof(ue_PyFTransform), /* tp_basicsize */ @@ -435,4 +435,4 @@ bool py_ue_transform_arg(PyObject *args, FTransform &t) t.SetRotation(FRotator(pitch, yaw, roll).Quaternion()); t.SetScale3D(FVector(sx, sy, sz)); return true; -} \ No newline at end of file +} diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.h index 3cb64312f..86f58e0f9 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFTransform.h @@ -10,8 +10,10 @@ typedef struct { FTransform transform; } ue_PyFTransform; +extern PyTypeObject ue_PyFTransformType; + PyObject *py_ue_new_ftransform(FTransform); ue_PyFTransform *py_ue_is_ftransform(PyObject *); void ue_python_init_ftransform(PyObject *); -bool py_ue_transform_arg(PyObject *, FTransform &); \ No newline at end of file +bool py_ue_transform_arg(PyObject *, FTransform &); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.cpp index 3380d82df..113c3bb62 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.cpp +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.cpp @@ -151,7 +151,7 @@ static PyObject *ue_PyFVector_str(ue_PyFVector *self) PyFloat_FromDouble(self->vec.X), PyFloat_FromDouble(self->vec.Y), PyFloat_FromDouble(self->vec.Z)); } -static PyTypeObject ue_PyFVectorType = { +PyTypeObject ue_PyFVectorType = { PyVarObject_HEAD_INIT(NULL, 0) "unreal_engine.FVector", /* tp_name */ sizeof(ue_PyFVector), /* tp_basicsize */ @@ -277,6 +277,24 @@ static PyObject *ue_py_fvector_div(ue_PyFVector *self, PyObject *value) return py_ue_new_fvector(vec); } +static PyObject *ue_py_fvector_floor_div(ue_PyFVector *self, PyObject *value) +{ + FVector vec = self->vec; + if (PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + float f = PyFloat_AsDouble(f_value); + if (f == 0) + return PyErr_Format(PyExc_ZeroDivisionError, "division by zero"); + vec.X = floor(vec.X / f); + vec.Y = floor(vec.Y / f); + vec.Z = floor(vec.Z / f); + Py_DECREF(f_value); + return py_ue_new_fvector(vec); + } + return PyErr_Format(PyExc_TypeError, "value is not numeric"); +} + PyNumberMethods ue_PyFVector_number_methods; static Py_ssize_t ue_py_fvector_seq_length(ue_PyFVector *self) @@ -363,7 +381,8 @@ void ue_python_init_fvector(PyObject *ue_module) ue_PyFVector_number_methods.nb_add = (binaryfunc)ue_py_fvector_add; ue_PyFVector_number_methods.nb_subtract = (binaryfunc)ue_py_fvector_sub; ue_PyFVector_number_methods.nb_multiply = (binaryfunc)ue_py_fvector_mul; - ue_PyFVector_number_methods.nb_divmod = (binaryfunc)ue_py_fvector_div; + ue_PyFVector_number_methods.nb_true_divide = (binaryfunc)ue_py_fvector_div; + ue_PyFVector_number_methods.nb_floor_divide = (binaryfunc)ue_py_fvector_floor_div; memset(&ue_PyFVector_sequence_methods, 0, sizeof(PySequenceMethods)); ue_PyFVectorType.tp_as_sequence = &ue_PyFVector_sequence_methods; diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.h index e23935943..b7b0853e8 100644 --- a/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.h +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector.h @@ -11,9 +11,11 @@ typedef struct FVector vec; } ue_PyFVector; +extern PyTypeObject ue_PyFVectorType; + PyObject *py_ue_new_fvector(FVector); ue_PyFVector *py_ue_is_fvector(PyObject *); void ue_python_init_fvector(PyObject *); -bool py_ue_vector_arg(PyObject *, FVector &); \ No newline at end of file +bool py_ue_vector_arg(PyObject *, FVector &); diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector2D.cpp b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector2D.cpp new file mode 100644 index 000000000..20855cf56 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector2D.cpp @@ -0,0 +1,368 @@ +#include "UEPyFVector2D.h" + +static PyObject *py_ue_fvector2d_length(ue_PyFVector2D *self, PyObject * args) +{ + return PyFloat_FromDouble(self->vec.Size()); +} + +static PyObject *py_ue_fvector2d_length_squared(ue_PyFVector2D *self, PyObject * args) +{ + return PyFloat_FromDouble(self->vec.SizeSquared()); +} + +static PyObject *py_ue_fvector2d_normalized(ue_PyFVector2D *self, PyObject * args) +{ + FVector2D vec = self->vec; + vec.Normalize(); + return py_ue_new_fvector2d(vec); +} + +static PyObject *py_ue_fvector2d_dot(ue_PyFVector2D *self, PyObject * args) +{ + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:dot", &py_obj)) + return NULL; + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(py_obj); + if (!py_vec) + return PyErr_Format(PyExc_TypeError, "argument is not a FVector2D"); + return PyFloat_FromDouble(FVector2D::DotProduct(self->vec, py_vec->vec)); +} + +static PyObject *py_ue_fvector2d_cross(ue_PyFVector2D *self, PyObject * args) +{ + PyObject *py_obj; + if (!PyArg_ParseTuple(args, "O:cross", &py_obj)) + return NULL; + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(py_obj); + if (!py_vec) + return PyErr_Format(PyExc_TypeError, "argument is not a FVector2D"); + return PyFloat_FromDouble(FVector2D::CrossProduct(self->vec, py_vec->vec)); +} + +static PyMethodDef ue_PyFVector2D_methods[] = { + + { "length", (PyCFunction)py_ue_fvector2d_length, METH_VARARGS, "" }, + { "size", (PyCFunction)py_ue_fvector2d_length, METH_VARARGS, "" }, + { "size_squared", (PyCFunction)py_ue_fvector2d_length_squared, METH_VARARGS, "" }, + { "length_squared", (PyCFunction)py_ue_fvector2d_length_squared, METH_VARARGS, "" }, + { "normalized", (PyCFunction)py_ue_fvector2d_normalized, METH_VARARGS, "" }, + { "dot", (PyCFunction)py_ue_fvector2d_dot, METH_VARARGS, "" }, + { "cross", (PyCFunction)py_ue_fvector2d_cross, METH_VARARGS, "" }, + { NULL } /* Sentinel */ +}; + +static PyObject *py_ue_fvector2d_get_x(ue_PyFVector2D *self, void *closure) +{ + return PyFloat_FromDouble(self->vec.X); +} + +static int py_ue_fvector2d_set_x(ue_PyFVector2D *self, PyObject *value, void *closure) +{ + if (value && PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + self->vec.X = PyFloat_AsDouble(f_value); + Py_DECREF(f_value); + return 0; + } + PyErr_SetString(PyExc_TypeError, "value is not numeric"); + return -1; +} + +static PyObject *py_ue_fvector2d_get_y(ue_PyFVector2D *self, void *closure) +{ + return PyFloat_FromDouble(self->vec.Y); +} + +static int py_ue_fvector2d_set_y(ue_PyFVector2D *self, PyObject *value, void *closure) +{ + if (value && PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + self->vec.Y = PyFloat_AsDouble(f_value); + Py_DECREF(f_value); + return 0; + } + PyErr_SetString(PyExc_TypeError, "value is not numeric"); + return -1; +} + + +static PyGetSetDef ue_PyFVector2D_getseters[] = { + {(char *) "x", (getter)py_ue_fvector2d_get_x, (setter)py_ue_fvector2d_set_x, (char *)"", NULL }, + {(char *) "y", (getter)py_ue_fvector2d_get_y, (setter)py_ue_fvector2d_set_y, (char *)"", NULL }, + { NULL } /* Sentinel */ +}; + +static PyObject *ue_PyFVector2D_str(ue_PyFVector2D *self) +{ + return PyUnicode_FromFormat("", + PyFloat_FromDouble(self->vec.X), PyFloat_FromDouble(self->vec.Y)); +} + +PyTypeObject ue_PyFVector2DType = { + PyVarObject_HEAD_INIT(NULL, 0) + "unreal_engine.FVector2D", /* tp_name */ + sizeof(ue_PyFVector2D), /* 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 */ + (reprfunc)ue_PyFVector2D_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#if PY_MAJOR_VERSION < 3 + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ +#else + Py_TPFLAGS_DEFAULT, /* tp_flags */ +#endif + "Unreal Engine FVector2D", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ue_PyFVector2D_methods, /* tp_methods */ + 0, + ue_PyFVector2D_getseters, +}; + + +static PyObject *ue_py_fvector2d_add(ue_PyFVector2D *self, PyObject *value) +{ + FVector2D vec = self->vec; + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(value); + if (py_vec) + { + vec += py_vec->vec; + } + else if (PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + float f = PyFloat_AsDouble(f_value); + vec.X += f; + vec.Y += f; + Py_DECREF(f_value); + } + return py_ue_new_fvector2d(vec); +} + +static PyObject *ue_py_fvector2d_sub(ue_PyFVector2D *self, PyObject *value) +{ + FVector2D vec = self->vec; + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(value); + if (py_vec) + { + vec -= py_vec->vec; + } + else if (PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + float f = PyFloat_AsDouble(f_value); + vec.X -= f; + vec.Y -= f; + Py_DECREF(f_value); + } + return py_ue_new_fvector2d(vec); +} + +static PyObject *ue_py_fvector2d_mul(ue_PyFVector2D *self, PyObject *value) +{ + FVector2D vec = self->vec; + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(value); + if (py_vec) + { + vec *= py_vec->vec; + } + else if (PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + float f = PyFloat_AsDouble(f_value); + vec *= f; + Py_DECREF(f_value); + } + return py_ue_new_fvector2d(vec); +} + +static PyObject *ue_py_fvector2d_div(ue_PyFVector2D *self, PyObject *value) +{ + FVector2D vec = self->vec; + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(value); + if (py_vec) + { + if (py_vec->vec.X == 0 || py_vec->vec.Y == 0) + return PyErr_Format(PyExc_ZeroDivisionError, "division by zero"); + vec /= py_vec->vec; + } + else if (PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + float f = PyFloat_AsDouble(f_value); + if (f == 0) + return PyErr_Format(PyExc_ZeroDivisionError, "division by zero"); + vec /= f; + Py_DECREF(f_value); + } + return py_ue_new_fvector2d(vec); +} + +static PyObject *ue_py_fvector2d_floor_div(ue_PyFVector2D *self, PyObject *value) +{ + FVector2D vec = self->vec; + if (PyNumber_Check(value)) + { + PyObject *f_value = PyNumber_Float(value); + float f = PyFloat_AsDouble(f_value); + if (f == 0) + return PyErr_Format(PyExc_ZeroDivisionError, "division by zero"); + vec.X = floor(vec.X / f); + vec.Y = floor(vec.Y / f); + Py_DECREF(f_value); + return py_ue_new_fvector2d(vec); + } + return PyErr_Format(PyExc_TypeError, "value is not numeric"); +} + +PyNumberMethods ue_PyFVector2D_number_methods; + +static Py_ssize_t ue_py_fvector2d_seq_length(ue_PyFVector2D *self) +{ + return 2; +} + +static PyObject *ue_py_fvector2d_seq_item(ue_PyFVector2D *self, Py_ssize_t i) +{ + switch (i) + { + case 0: + return PyFloat_FromDouble(self->vec.X); + case 1: + return PyFloat_FromDouble(self->vec.Y); + } + return PyErr_Format(PyExc_IndexError, "FVector2D has only 2 items"); +} + +PySequenceMethods ue_PyFVector2D_sequence_methods; + +static int ue_py_fvector2d_init(ue_PyFVector2D *self, PyObject *args, PyObject *kwargs) +{ + float x = 0, y = 0; + if (!PyArg_ParseTuple(args, "|ff", &x, &y)) + return -1; + + if (PyTuple_Size(args) == 1) + { + y = x; + } + + self->vec.X = x; + self->vec.Y = y; + + return 0; +} + +static PyObject *ue_py_fvector2d_richcompare(ue_PyFVector2D *vec1, PyObject *b, int op) +{ + ue_PyFVector2D *vec2 = py_ue_is_fvector2d(b); + if (!vec2 || (op != Py_EQ && op != Py_NE)) + { + return PyErr_Format(PyExc_NotImplementedError, "can only compare with another FVector2D"); + } + + if (op == Py_EQ) + { + if (vec1->vec.X == vec2->vec.X && + vec1->vec.Y == vec2->vec.Y) + { + Py_INCREF(Py_True); + return Py_True; + } + Py_INCREF(Py_False); + return Py_False; + } + + if (vec1->vec.X == vec2->vec.X && + vec1->vec.Y == vec2->vec.Y) + { + Py_INCREF(Py_False); + return Py_False; + } + Py_INCREF(Py_True); + return Py_True; +} + +void ue_python_init_fvector2d(PyObject *ue_module) +{ + ue_PyFVector2DType.tp_new = PyType_GenericNew; + + ue_PyFVector2DType.tp_init = (initproc)ue_py_fvector2d_init; + ue_PyFVector2DType.tp_richcompare = (richcmpfunc)ue_py_fvector2d_richcompare; + + memset(&ue_PyFVector2D_number_methods, 0, sizeof(PyNumberMethods)); + ue_PyFVector2DType.tp_as_number = &ue_PyFVector2D_number_methods; + ue_PyFVector2D_number_methods.nb_add = (binaryfunc)ue_py_fvector2d_add; + ue_PyFVector2D_number_methods.nb_subtract = (binaryfunc)ue_py_fvector2d_sub; + ue_PyFVector2D_number_methods.nb_multiply = (binaryfunc)ue_py_fvector2d_mul; + ue_PyFVector2D_number_methods.nb_true_divide = (binaryfunc)ue_py_fvector2d_div; + ue_PyFVector2D_number_methods.nb_floor_divide = (binaryfunc)ue_py_fvector2d_floor_div; + + memset(&ue_PyFVector2D_sequence_methods, 0, sizeof(PySequenceMethods)); + ue_PyFVector2DType.tp_as_sequence = &ue_PyFVector2D_sequence_methods; + ue_PyFVector2D_sequence_methods.sq_length = (lenfunc)ue_py_fvector2d_seq_length; + ue_PyFVector2D_sequence_methods.sq_item = (ssizeargfunc)ue_py_fvector2d_seq_item; + + if (PyType_Ready(&ue_PyFVector2DType) < 0) + return; + + Py_INCREF(&ue_PyFVector2DType); + PyModule_AddObject(ue_module, "FVector2D", (PyObject *)&ue_PyFVector2DType); +} + +PyObject *py_ue_new_fvector2d(FVector2D vec) +{ + ue_PyFVector2D *ret = (ue_PyFVector2D *)PyObject_New(ue_PyFVector2D, &ue_PyFVector2DType); + ret->vec = vec; + return (PyObject *)ret; +} + +ue_PyFVector2D *py_ue_is_fvector2d(PyObject *obj) +{ + if (!PyObject_IsInstance(obj, (PyObject *)&ue_PyFVector2DType)) + return nullptr; + return (ue_PyFVector2D *)obj; +} + +bool py_ue_vector2d_arg(PyObject *args, FVector2D &vec) +{ + + if (PyTuple_Size(args) == 1) + { + PyObject *arg = PyTuple_GetItem(args, 0); + ue_PyFVector2D *py_vec = py_ue_is_fvector2d(arg); + if (!py_vec) + { + PyErr_Format(PyExc_TypeError, "argument is not a FVector2D"); + return false; + } + vec = py_vec->vec; + return true; + } + + float x, y; + if (!PyArg_ParseTuple(args, "ff", &x, &y)) + return false; + vec.X = x; + vec.Y = y; + return true; +} + diff --git a/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector2D.h b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector2D.h new file mode 100644 index 000000000..b9c2f4d73 --- /dev/null +++ b/Source/UnrealEnginePython/Private/Wrappers/UEPyFVector2D.h @@ -0,0 +1,21 @@ +#pragma once + + + +#include "UEPyModule.h" + +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + FVector2D vec; +} ue_PyFVector2D; + +extern PyTypeObject ue_PyFVector2DType; + +PyObject *py_ue_new_fvector2d(FVector2D); +ue_PyFVector2D *py_ue_is_fvector2d(PyObject *); + +void ue_python_init_fvector2d(PyObject *); + +bool py_ue_vector2d_arg(PyObject *, FVector2D &); diff --git a/Source/UnrealEnginePython/Public/PythonDelegate.h b/Source/UnrealEnginePython/Public/PythonDelegate.h index 01d0f70b7..be46aa511 100644 --- a/Source/UnrealEnginePython/Public/PythonDelegate.h +++ b/Source/UnrealEnginePython/Public/PythonDelegate.h @@ -13,6 +13,7 @@ class UPythonDelegate : public UObject ~UPythonDelegate(); virtual void ProcessEvent(UFunction *function, void *Parms) override; void SetPyCallable(PyObject *callable); + bool UsesPyCallable(PyObject *callable); void SetSignature(UFunction *original_signature); void PyInputHandler(); diff --git a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h index ed36ecc63..ccd9edb01 100644 --- a/Source/UnrealEnginePython/Public/PythonHouseKeeper.h +++ b/Source/UnrealEnginePython/Public/PythonHouseKeeper.h @@ -11,293 +11,72 @@ class FUnrealEnginePythonHouseKeeper : public FGCObject { - - struct FPythonUOjectTracker - { - FWeakObjectPtr Owner; - ue_PyUObject *PyUObject; - bool bPythonOwned; - - FPythonUOjectTracker(UObject *Object, ue_PyUObject *InPyUObject) - { - Owner = FWeakObjectPtr(Object); - PyUObject = InPyUObject; - bPythonOwned = false; - } - }; - - struct FPythonDelegateTracker - { - FWeakObjectPtr Owner; - UPythonDelegate *Delegate; - - FPythonDelegateTracker(UPythonDelegate *DelegateToTrack, UObject *DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) - { - } - - ~FPythonDelegateTracker() - { - } - }; - - - struct FPythonSWidgetDelegateTracker - { - TWeakPtr Owner; - TSharedPtr Delegate; - - FPythonSWidgetDelegateTracker(TSharedRef DelegateToTrack, TSharedRef DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) - { - } - - ~FPythonSWidgetDelegateTracker() - { - } - }; + struct FPythonUOjectTracker + { + FWeakObjectPtr Owner; + ue_PyUObject *PyUObject; + bool bPythonOwned; + + FPythonUOjectTracker(UObject *Object, ue_PyUObject *InPyUObject) + { + Owner = FWeakObjectPtr(Object); + PyUObject = InPyUObject; + bPythonOwned = false; + } + }; + + struct FPythonDelegateTracker + { + FWeakObjectPtr Owner; + UPythonDelegate *Delegate; + + FPythonDelegateTracker(UPythonDelegate *DelegateToTrack, UObject *DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) + { + } + + ~FPythonDelegateTracker() + { + } + }; + + struct FPythonSWidgetDelegateTracker + { + TWeakPtr Owner; + TSharedPtr Delegate; + + FPythonSWidgetDelegateTracker(TSharedRef DelegateToTrack, TSharedRef DelegateOwner) : Owner(DelegateOwner), Delegate(DelegateToTrack) + { + } + + ~FPythonSWidgetDelegateTracker() + { + } + }; public: - virtual void AddReferencedObjects(FReferenceCollector& InCollector) override - { - InCollector.AddReferencedObjects(PythonTrackedObjects); - } - - static FUnrealEnginePythonHouseKeeper *Get() - { - static FUnrealEnginePythonHouseKeeper *Singleton; - if (!Singleton) - { - Singleton = new FUnrealEnginePythonHouseKeeper(); - // register a new delegate for the GC -#if ENGINE_MINOR_VERSION >= 18 - FCoreUObjectDelegates::GetPostGarbageCollect().AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); -#else - FCoreUObjectDelegates::PostGarbageCollect.AddRaw(Singleton, &FUnrealEnginePythonHouseKeeper::RunGCDelegate); -#endif - } - return Singleton; - } - - void RunGCDelegate() - { - FScopePythonGIL gil; - RunGC(); - } - - int32 RunGC() - { - int32 Garbaged = PyUObjectsGC(); - Garbaged += DelegatesGC(); - return Garbaged; - } - - bool IsValidPyUObject(ue_PyUObject *PyUObject) - { - if (!PyUObject) - return false; - - UObject *Object = PyUObject->ue_object; - FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); - if (!Tracker) - { - return false; - } - - if (!Tracker->Owner.IsValid()) - return false; - - return true; - - } - - void TrackUObject(UObject *Object) - { - FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); - if (!Tracker) - { - return; - } - if (Tracker->bPythonOwned) - return; - Tracker->bPythonOwned = true; - // when a new ue_PyUObject spawns, it has a reference counting of two - Py_DECREF(Tracker->PyUObject); - Tracker->PyUObject->owned = 1; - PythonTrackedObjects.Add(Object); - } - - void UntrackUObject(UObject *Object) - { - PythonTrackedObjects.Remove(Object); - } - - void RegisterPyUObject(UObject *Object, ue_PyUObject *InPyUObject) - { - UObjectPyMapping.Add(Object, FPythonUOjectTracker(Object, InPyUObject)); - } - - void UnregisterPyUObject(UObject *Object) - { - UObjectPyMapping.Remove(Object); - } - - ue_PyUObject *GetPyUObject(UObject *Object) - { - FPythonUOjectTracker *Tracker = UObjectPyMapping.Find(Object); - if (!Tracker) - { - return nullptr; - } - - if (!Tracker->Owner.IsValid(true)) - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("DEFREF'ing UObject at %p (refcnt: %d)"), Object, Tracker->PyUObject->ob_base.ob_refcnt); -#endif - if (!Tracker->bPythonOwned) - Py_DECREF((PyObject *)Tracker->PyUObject); - UnregisterPyUObject(Object); - return nullptr; - } - - return Tracker->PyUObject; -} - - uint32 PyUObjectsGC() - { - uint32 Garbaged = 0; - TArray BrokenList; - for (auto &UObjectPyItem : UObjectPyMapping) - { - UObject *Object = UObjectPyItem.Key; - FPythonUOjectTracker &Tracker = UObjectPyItem.Value; -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Checking for UObject at %p"), Object); -#endif - if (!Tracker.Owner.IsValid(true)) - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Warning, TEXT("Removing UObject at %p (refcnt: %d)"), Object, Tracker.PyUObject->ob_base.ob_refcnt); -#endif - BrokenList.Add(Object); - Garbaged++; - } - else - { -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Error, TEXT("UObject at %p %s is in use"), Object, *Object->GetName()); -#endif - } - } - - for (UObject *Object : BrokenList) - { - FPythonUOjectTracker &Tracker = UObjectPyMapping[Object]; - if (!Tracker.bPythonOwned) - Py_DECREF((PyObject *)Tracker.PyUObject); - UnregisterPyUObject(Object); - } - - return Garbaged; - - } - - - int32 DelegatesGC() - { - int32 Garbaged = 0; -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Display, TEXT("Garbage collecting %d UObject delegates"), PyDelegatesTracker.Num()); -#endif - for (int32 i = PyDelegatesTracker.Num() - 1; i >= 0; --i) - { - FPythonDelegateTracker &Tracker = PyDelegatesTracker[i]; - if (!Tracker.Owner.IsValid(true)) - { - Tracker.Delegate->RemoveFromRoot(); - PyDelegatesTracker.RemoveAt(i); - Garbaged++; - } - - } - -#if defined(UEPY_MEMORY_DEBUG) - UE_LOG(LogPython, Display, TEXT("Garbage collecting %d Slate delegates"), PySlateDelegatesTracker.Num()); -#endif - - for (int32 i = PySlateDelegatesTracker.Num() - 1; i >= 0; --i) - { - FPythonSWidgetDelegateTracker &Tracker = PySlateDelegatesTracker[i]; - if (!Tracker.Owner.IsValid()) - { - PySlateDelegatesTracker.RemoveAt(i); - Garbaged++; - } - - } - return Garbaged; - } - - UPythonDelegate *NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature) - { - UPythonDelegate *Delegate = NewObject(); - - Delegate->AddToRoot(); - Delegate->SetPyCallable(PyCallable); - Delegate->SetSignature(Signature); - - FPythonDelegateTracker Tracker(Delegate, Owner); - PyDelegatesTracker.Add(Tracker); - - return Delegate; - } - - TSharedRef NewSlateDelegate(TSharedRef Owner, PyObject *PyCallable) - { - TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); - Delegate->SetPyCallable(PyCallable); - - FPythonSWidgetDelegateTracker Tracker(Delegate, Owner); - PySlateDelegatesTracker.Add(Tracker); - - return Delegate; - } - - TSharedRef NewDeferredSlateDelegate(PyObject *PyCallable) - { - TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); - Delegate->SetPyCallable(PyCallable); - - return Delegate; - } - - TSharedRef NewPythonSmartDelegate(PyObject *PyCallable) - { - TSharedRef Delegate = MakeShareable(new FPythonSmartDelegate()); - Delegate->SetPyCallable(PyCallable); - - PyStaticSmartDelegatesTracker.Add(Delegate); - - return Delegate; - } - - void TrackDeferredSlateDelegate(TSharedRef Delegate, TSharedRef Owner) - { - FPythonSWidgetDelegateTracker Tracker(Delegate, Owner); - PySlateDelegatesTracker.Add(Tracker); - } - - TSharedRef NewStaticSlateDelegate(PyObject *PyCallable) - { - TSharedRef Delegate = MakeShareable(new FPythonSlateDelegate()); - Delegate->SetPyCallable(PyCallable); - - PyStaticSlateDelegatesTracker.Add(Delegate); - - return Delegate; - } + virtual void AddReferencedObjects(FReferenceCollector& InCollector) override; + static FUnrealEnginePythonHouseKeeper *Get(); + int32 RunGC(); + bool IsValidPyUObject(ue_PyUObject *PyUObject); + void TrackUObject(UObject *Object); + void UntrackUObject(UObject *Object); + void RegisterPyUObject(UObject *Object, ue_PyUObject *InPyUObject); + void UnregisterPyUObject(UObject *Object); + ue_PyUObject *GetPyUObject(UObject *Object); + UPythonDelegate *FindDelegate(UObject *Owner, PyObject *PyCallable); + UPythonDelegate *NewDelegate(UObject *Owner, PyObject *PyCallable, UFunction *Signature); + TSharedRef NewSlateDelegate(TSharedRef Owner, PyObject *PyCallable); + TSharedRef NewDeferredSlateDelegate(PyObject *PyCallable); + TSharedRef NewPythonSmartDelegate(PyObject *PyCallable); + void TrackDeferredSlateDelegate(TSharedRef Delegate, TSharedRef Owner); + TSharedRef NewStaticSlateDelegate(PyObject *PyCallable); private: + void RunGCDelegate(); + uint32 PyUObjectsGC(); + int32 DelegatesGC(); + TMap UObjectPyMapping; TArray PyDelegatesTracker; diff --git a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs index 6e7727d0b..a6fe71946 100644 --- a/Source/UnrealEnginePython/UnrealEnginePython.Build.cs +++ b/Source/UnrealEnginePython/UnrealEnginePython.Build.cs @@ -14,11 +14,11 @@ public class UnrealEnginePython : ModuleRules // this is an example for Homebrew on Mac //private string pythonHome = "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/"; // on Linux an include;libs syntax is expected: - //private string pythonHome = "/usr/local/include/python3.6;/usr/local/lib/libpython3.6.so" + //private string pythonHome = "/usr/local/include/python3.6;/usr/local/lib/libpython3.6.so"; private string[] windowsKnownPaths = { - "C:/Program Files/Python37", + // "C:/Program Files/Python37", "C:/Program Files/Python36", "C:/Program Files/Python35", "C:/Python27", @@ -100,7 +100,6 @@ public UnrealEnginePython(TargetInfo Target) PublicIncludePaths.AddRange( new string[] { - "UnrealEnginePython/Public", // ... add public include paths required here ... } ); @@ -108,7 +107,6 @@ public UnrealEnginePython(TargetInfo Target) PrivateIncludePaths.AddRange( new string[] { - "UnrealEnginePython/Private", // ... add other private include paths required here ... } ); @@ -292,6 +290,11 @@ private string DiscoverPythonPath(string[] knownPaths, string binaryPath) if (!string.IsNullOrEmpty(environmentPath)) paths.Insert(0, environmentPath); + // look in an alternate custom location + environmentPath = System.Environment.GetEnvironmentVariable("UNREALENGINEPYTHONHOME"); + if (!string.IsNullOrEmpty(environmentPath)) + paths.Insert(0, environmentPath); + foreach (string path in paths) { string actualPath = path; diff --git a/examples/asset_metadata_tag.py b/examples/asset_metadata_tag.py new file mode 100644 index 000000000..dfa036f36 --- /dev/null +++ b/examples/asset_metadata_tag.py @@ -0,0 +1,19 @@ +import unreal_engine as ue +from unreal_engine.classes import EditorAssetLibrary + +asset = ue.get_selected_assets()[0] + +# reflection-based api +EditorAssetLibrary.SetMetadataTag(asset, 'Foo', 'Bar') +for value in EditorAssetLibrary.GetMetadataTagValues(asset): + print(value) +print(EditorAssetLibrary.GetMetadataTag(asset, 'Foo')) + + +# native api +asset.set_metadata_tag('Test001', 'Text002') +asset.set_metadata_tag('Test003', 'Text004') +for key in asset.metadata_tags(): + print(key) +print(asset.has_metadata_tag('Foo')) # bool +print(asset.get_metadata_tag('Test001')) diff --git a/examples/edit_level_blueprint.py b/examples/edit_level_blueprint.py new file mode 100644 index 000000000..a7ddd961c --- /dev/null +++ b/examples/edit_level_blueprint.py @@ -0,0 +1,8 @@ +import unreal_engine as ue +from unreal_engine.structs import EdGraphPinType + +world = ue.get_editor_world() +level_bp = world.CurrentLevel.get_level_script_blueprint() +pin = EdGraphPinType(PinCategory='string') +ue.blueprint_add_member_variable(level_bp, 'TestString', pin) +ue.open_editor_for_asset(level_bp) diff --git a/examples/sub_menu.py b/examples/sub_menu.py new file mode 100644 index 000000000..9d7901f2f --- /dev/null +++ b/examples/sub_menu.py @@ -0,0 +1,30 @@ +import unreal_engine as ue + +def open_submenu001(builder): + builder.begin_section('submenu001', 'i am a tooltip') + builder.add_menu_entry('sub_one', 'tooltip', lambda: ue.log('hello from submenu001')) + builder.add_menu_entry('sub_one_2', 'tooltip 2', lambda: ue.log('hello again')) + builder.end_section() + +def open_sub_submenu(builder): + builder.begin_section('sub_submenu003', 'i am a tooltip for the submenu') + builder.add_menu_entry('sub_sub_three', 'tooltip', lambda: ue.log('hello from sub_submenu003')) + builder.end_section() + +def open_submenu002(builder): + builder.begin_section('submenu002', 'i am a tooltip') + builder.add_menu_entry('sub_two', 'tooltip', lambda: ue.log('hello from submenu002')) + builder.add_sub_menu('sub sub menu', 'tooltip !', open_sub_submenu) + builder.end_section() + + +def open_menu(builder): + builder.begin_section('test1', 'test2') + builder.add_menu_entry('one', 'two', lambda: ue.log('ciao 1')) + builder.add_sub_menu('i am a submenu', 'tooltip for the submenu', open_submenu001) + builder.add_menu_entry('three', 'four', lambda: ue.log('ciao 2')) + builder.add_sub_menu('i am another submenu', 'tooltip for the second submenu', open_submenu002) + builder.end_section() + + +ue.add_menu_bar_extension('SimpleMenuBarExtension', open_menu) \ No newline at end of file diff --git a/tools/release_win64.py b/tools/release_win64.py index c07ffa3f7..057800496 100644 --- a/tools/release_win64.py +++ b/tools/release_win64.py @@ -5,8 +5,11 @@ import shutil import zipfile -UE_VERSIONS = ['4.15', '4.16', '4.17', '4.18', '4.19', '4.20'] -PYTHON_VERSIONS = ["C:/Program Files/Python36", "C:/Program Files/Python37", "C:/Python27"] +UE_VERSIONS = ['4.20', '4.21', '4.22'] +PYTHON_VERSIONS = ["C:/Program Files/Python37", "C:/Program Files/Python36", "C:/Python27"] +MSBUILD = 'C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/MSBuild/15.0/Bin/MSBuild.exe' +UE_PATH = 'C:/Program Files/Epic Games' +PROJECTS_PATH = 'C:/Users/rober/Documents/Unreal Projects' RELEASE_DIR = sys.argv[1].rstrip('/') @@ -20,28 +23,27 @@ def msbuild(project, python_version): base_environ = os.environ base_environ.update({'PYTHONHOME': python_version}) base_environ.update({'UEP_ENABLE_UNITY_BUILD': '1'}) - #vs = '"C:/Program Files (x86)/MSBuild/14.0/Bin/MSBuild.exe"' - vs = '"C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/MSBuild/15.0/Bin/MSBuild.exe"' - process = subprocess.Popen('{0} {1} /m /t:Rebuild /p:Configuration="Development Editor" /p:Platform=Win64'.format(vs, project), env=base_environ) + vs = '"{}"'.format(MSBUILD) + process = subprocess.Popen('{0} "{1}" /m /t:Rebuild /p:Configuration="Development Editor" /p:Platform=Win64'.format(vs, project), env=base_environ) while process.poll() is None: time.sleep(0.5) if process.returncode != 0: sys.exit(process.returncode) def commandlet(version, project): - ue_editor = os.path.join('D:/', 'UE_{0}'.format(version), 'Engine/Binaries/Win64/UE4Editor-Cmd.exe') - process = subprocess.Popen('{0} D:/{1}/{2}.uproject -run=PyCommandlet D:/{3}/Plugins/UnrealEnginePython/tools/release_check.py'.format(ue_editor, project, project, project)) + ue_editor = os.path.join(UE_PATH, 'UE_{0}'.format(version), 'Engine/Binaries/Win64/UE4Editor-Cmd.exe') + process = subprocess.Popen('{0} {1}/{2}/{3}.uproject -run=PyCommandlet {1}/{4}/Plugins/UnrealEnginePython/tools/release_check.py'.format(ue_editor, PROJECTS_PATH, project, project, project)) while process.poll() is None: time.sleep(0.5) # ignore return code, has too much different meanings for commandlets def git(project): - process = subprocess.Popen('git checkout master', cwd='D:/{0}/Plugins/UnrealEnginePython'.format(project)) + process = subprocess.Popen('git checkout master', cwd='{0}/{1}/Plugins/UnrealEnginePython'.format(PROJECTS_PATH, project)) while process.poll() is None: time.sleep(0.5) if process.returncode != 0: sys.exit(process.returncode) - process = subprocess.Popen('git pull', cwd='D:/{0}/Plugins/UnrealEnginePython'.format(project)) + process = subprocess.Popen('git pull', cwd='{0}/{1}/Plugins/UnrealEnginePython'.format(PROJECTS_PATH, project)) while process.poll() is None: time.sleep(0.5) if process.returncode != 0: @@ -51,7 +53,7 @@ def git(project): main_start = time.time() for ue_version in UE_VERSIONS: project = 'PyTest{0}'.format(ue_version.replace('.', '')) - sln = os.path.join('D:/', project, '{0}.sln'.format(project)) + sln = os.path.join(PROJECTS_PATH, project, '{0}.sln'.format(project)) git(project) for python_version in PYTHON_VERSIONS: python_sanitized = os.path.basename(python_version).lower() @@ -62,9 +64,9 @@ def git(project): commandlet(ue_version, project) end = time.time() for item in ('UE4Editor.modules', 'UE4Editor-UnrealEnginePython.dll', 'UE4Editor-PythonConsole.dll', 'UE4Editor-PythonEditor.dll', 'UE4Editor-PythonAutomation.dll'): - shutil.copyfile('D:/{0}/Plugins/UnrealEnginePython/Binaries/Win64/{1}'.format(project, item), '{0}/UnrealEnginePython/Binaries/Win64/{1}'.format(RELEASE_DIR, item)) + shutil.copyfile('{0}/{1}/Plugins/UnrealEnginePython/Binaries/Win64/{2}'.format(PROJECTS_PATH, project, item), '{0}/UnrealEnginePython/Binaries/Win64/{1}'.format(RELEASE_DIR, item)) if python_sanitized == 'python36': - shutil.copyfile('D:/{0}/Plugins/UnrealEnginePython/Binaries/Win64/{1}'.format(project, item), '{0}/Embedded/UnrealEnginePython/Binaries/Win64/{1}'.format(RELEASE_DIR, item)) + shutil.copyfile('{0}/{1}/Plugins/UnrealEnginePython/Binaries/Win64/{2}'.format(PROJECTS_PATH, project, item), '{0}/Embedded/UnrealEnginePython/Binaries/Win64/{1}'.format(RELEASE_DIR, item)) filename = 'UnrealEnginePython_{0}_{1}_{2}_win64.zip'.format(os.path.basename(RELEASE_DIR), ue_version.replace('.','_'), python_sanitized) zh = zipfile.ZipFile(os.path.join(RELEASE_DIR, filename), 'w', zipfile.ZIP_DEFLATED) zipdir(os.path.join(RELEASE_DIR, 'UnrealEnginePython'), zh, RELEASE_DIR) diff --git a/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython.md b/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython.md index 8ff7795c7..4d9c5246a 100644 --- a/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython.md +++ b/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython.md @@ -194,7 +194,7 @@ class PlotComponent: dpi = 72.0 self.texture = ue.create_transient_texture(width, height, EPixelFormat.PF_R8G8B8A8) - self.uobject.get_owner().StaticMesh.OverrideMaterials[0].set_material_texture_parameter('Graph', self.texture) + self.uobject.get_owner().StaticMeshComponent.OverrideMaterials[0].set_material_texture_parameter('Graph', self.texture) self.fig = plt.figure(1) self.fig.set_dpi(dpi) diff --git a/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython_Assets/plotter.py b/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython_Assets/plotter.py index e8e8418a4..6466adee2 100644 --- a/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython_Assets/plotter.py +++ b/tutorials/PlottingGraphsWithMatplotlibAndUnrealEnginePython_Assets/plotter.py @@ -14,7 +14,7 @@ def begin_play(self): dpi = 72.0 self.texture = ue.create_transient_texture(width, height, EPixelFormat.PF_R8G8B8A8) - self.uobject.get_owner().StaticMesh.OverrideMaterials[0].set_material_texture_parameter('Graph', self.texture) + self.uobject.get_owner().StaticMeshComponent.OverrideMaterials[0].set_material_texture_parameter('Graph', self.texture) self.fig = plt.figure(1) self.fig.set_dpi(dpi)