From bed4f3e805713494bdd5811555fa9cdbce665fcc Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 24 Feb 2025 16:46:05 -0500 Subject: [PATCH] Lots of work on lua call interface, also improved makefiles --- Content/Tangibles/tangiblecharacter.uasset | 4 +- Integration.code-workspace.old | 868 +++++++++++++++++++++ Source/Integration/FormatError.cpp | 2 +- Source/Integration/LuaCall.cpp | 57 +- Source/Integration/LuaCall.h | 22 +- Source/Integration/LuaCallNode.cpp | 697 +++++++++++++++++ Source/Integration/LuaCallNode.h | 87 +++ build-everything.py | 219 +++--- luprex/cpp/core/world-core.cpp | 11 +- luprex/lua/login.lua | 4 + 10 files changed, 1870 insertions(+), 101 deletions(-) create mode 100644 Integration.code-workspace.old create mode 100644 Source/Integration/LuaCallNode.cpp create mode 100644 Source/Integration/LuaCallNode.h diff --git a/Content/Tangibles/tangiblecharacter.uasset b/Content/Tangibles/tangiblecharacter.uasset index 52ccc390..1c5bfc5e 100644 --- a/Content/Tangibles/tangiblecharacter.uasset +++ b/Content/Tangibles/tangiblecharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e867bfbaed611387e5649533a7dafc441163cddd09908621422ea5ca334d6ac2 -size 367701 +oid sha256:46db59a0feeb2790d2fa724a7616371722e8308334271205c033272e607cb6fb +size 332344 diff --git a/Integration.code-workspace.old b/Integration.code-workspace.old new file mode 100644 index 00000000..98d9d561 --- /dev/null +++ b/Integration.code-workspace.old @@ -0,0 +1,868 @@ +{ + "folders": [ + { + "name": "Integration", + "path": "." + }, + { + "name": "UE5", + "path": "/home/jyelon/UnrealEngine" + } + ], + "settings": { + "typescript.tsc.autoDetect": "off", + "npm.autoDetect": "off" + }, + "extensions": { + "recommendations": [ + "ms-vscode.cpptools", + "ms-dotnettools.csharp", + "vadimcn.vscode-lldb", + "ms-vscode.mono-debug", + "dfarley1.file-picker" + ] + }, + "tasks": { + "version": "2.0.0", + "tasks": [ + { + "label": "Integration Linux Debug Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Debug Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration Linux Debug Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Debug Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux DebugGame Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux DebugGame Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration Linux DebugGame Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux DebugGame Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Development Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Development Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration Linux Development Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Development Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Test Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Test", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Test Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Test", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration Linux Test Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Test Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Test", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Shipping Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Shipping", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Shipping Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Shipping", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration Linux Shipping Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration Linux Shipping Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "Linux", + "Shipping", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Debug Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Debug Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration LinuxArm64 Debug Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Debug Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 DebugGame Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 DebugGame Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration LinuxArm64 DebugGame Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 DebugGame Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Development Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Development Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration LinuxArm64 Development Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Development Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Test Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Test", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Test Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Test", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration LinuxArm64 Test Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Test Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Test", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Shipping Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Shipping", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Shipping Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Shipping", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "Integration LinuxArm64 Shipping Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "Integration LinuxArm64 Shipping Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "Integration", + "LinuxArm64", + "Shipping", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux Debug Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux Debug Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "IntegrationEditor Linux Debug Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux Debug Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "Debug", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux DebugGame Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux DebugGame Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "IntegrationEditor Linux DebugGame Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux DebugGame Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "DebugGame", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux Development Build", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux Development Rebuild", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex" + ], + "problemMatcher": "$msCompile", + "dependsOn": [ + "IntegrationEditor Linux Development Clean" + ], + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + }, + { + "label": "IntegrationEditor Linux Development Clean", + "group": "build", + "command": "Engine/Build/BatchFiles/Linux/Build.sh", + "args": [ + "IntegrationEditor", + "Linux", + "Development", + "/home/jyelon/integration/Integration.uproject", + "-waitmutex", + "-clean" + ], + "problemMatcher": "$msCompile", + "type": "shell", + "options": { + "cwd": "/home/jyelon/UnrealEngine" + } + } + ] + }, + "launch": { + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Integration (Debug)", + "request": "launch", + "program": "/home/jyelon/integration/Binaries/Linux/Integration-Linux-Debug", + "preLaunchTask": "Integration Linux Debug Build", + "args": [ + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch Integration (DebugGame)", + "request": "launch", + "program": "/home/jyelon/integration/Binaries/Linux/Integration-Linux-DebugGame", + "preLaunchTask": "Integration Linux DebugGame Build", + "args": [ + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch Integration (Development)", + "request": "launch", + "program": "/home/jyelon/integration/Binaries/Linux/Integration", + "preLaunchTask": "Integration Linux Development Build", + "args": [ + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch Integration (Test)", + "request": "launch", + "program": "/home/jyelon/integration/Binaries/Linux/Integration-Linux-Test", + "preLaunchTask": "Integration Linux Test Build", + "args": [ + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch Integration (Shipping)", + "request": "launch", + "program": "/home/jyelon/integration/Binaries/Linux/Integration-Linux-Shipping", + "preLaunchTask": "Integration Linux Shipping Build", + "args": [ + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch IntegrationEditor (Debug)", + "request": "launch", + "program": "/home/jyelon/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Linux-Debug", + "preLaunchTask": "IntegrationEditor Linux Debug Build", + "args": [ + "/home/jyelon/integration/Integration.uproject" + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch IntegrationEditor (DebugGame)", + "request": "launch", + "program": "/home/jyelon/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Linux-DebugGame", + "preLaunchTask": "IntegrationEditor Linux DebugGame Build", + "args": [ + "/home/jyelon/integration/Integration.uproject" + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Launch IntegrationEditor (Development)", + "request": "launch", + "program": "/home/jyelon/UnrealEngine/Engine/Binaries/Linux/UnrealEditor", + "preLaunchTask": "IntegrationEditor Linux Development Build", + "args": [ + "/home/jyelon/integration/Integration.uproject" + ], + "cwd": "/home/jyelon/UnrealEngine", + "type": "cppdbg", + "visualizerFile": "/home/jyelon/UnrealEngine/Engine/Extras/VisualStudioDebugging/Unreal.natvis", + "showDisplayString": true + }, + { + "name": "Generate Project Files", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "UnrealBuildTool Linux Development Build", + "program": "/home/jyelon/UnrealEngine/Engine/Build/BatchFiles/RunUBT.bat", + "args": [ + "-projectfiles", + "-vscode", + "-project=/home/jyelon/integration/Integration.uproject", + "-game", + "-engine", + "-dotnet" + ], + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart", + "stopAtEntry": false, + "cwd": "/home/jyelon/UnrealEngine" + } + ] + } +} diff --git a/Source/Integration/FormatError.cpp b/Source/Integration/FormatError.cpp index f1de0e29..17b4350b 100644 --- a/Source/Integration/FormatError.cpp +++ b/Source/Integration/FormatError.cpp @@ -267,7 +267,7 @@ FText UK2Node_FormatError::GetTooltipText() const return NodeTooltip; } -UEdGraphPin* FindOutputStructPinChecked(UEdGraphNode* Node) +static UEdGraphPin* FindOutputStructPinChecked(UEdGraphNode* Node) { check(NULL != Node); UEdGraphPin* OutputPin = NULL; diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 4fae279f..d0b7cd03 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -13,6 +13,37 @@ static void CheckNotEmpty(const FlxStreamBuffer &sb) { } } +static constexpr uint64_t ParseNameAsToken(std::string_view str) { + uint64_t result = 0; + uint64_t maxint = uint64_t(-1); + + // Leading zeros are not allowed. + if ((!str.empty()) && (str[0]=='0')) return 0; + + for (int i = 0; i < int(str.size()); i++) { + char c = str[i]; + uint64_t digit = 0; + if ((c >= '0') && (c <= '9')) { + digit = uint64_t(c - '0'); + } else if ((c >= 'a') && (c <= 'z')) { + digit = uint64_t(c - 'a' + 10); + } else if ((c >= 'A') && (c <= 'Z')) { + digit = uint64_t(c - 'A' + 10); + } else { + return maxint; + } + // Multiply existing number by 36, then add the digit. + // We have two checks to prevent integer overflow. + if (result > (maxint / 36)) return 0; + result *= 36; + if (digit > (maxint - result)) return 0; + result += digit; + } + return result; +} + + + void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); @@ -31,11 +62,17 @@ void UlxLuaCallLibrary::LuaCallAddStringParameter(UObject *context, const FStrin } void UlxLuaCallLibrary::LuaCallAddNameParameter(UObject *context, const FName &pname) { + FTCHARToUTF8 utf8str(pname.ToString()); + std::string_view namestr(utf8str.Get(), utf8str.Length()); + uint64_t tokvalue = ParseNameAsToken(namestr); + if ((tokvalue == 0) && !namestr.empty()) { + FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); + } AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); CheckNotEmpty(sb); - sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); - sb.write_fname(pname); + sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); + sb.write_string(namestr); } void UlxLuaCallLibrary::LuaCallAddFloatParameter(UObject *context, double pfloat) { @@ -46,6 +83,13 @@ void UlxLuaCallLibrary::LuaCallAddFloatParameter(UObject *context, double pfloat sb.write_double(pfloat); } +void UlxLuaCallLibrary::LuaCallAddIntParameter(UObject *context, int value) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); + sb.write_double(value); +} void UlxLuaCallLibrary::LuaCallAddVectorParameter(UObject *context, const FVector &pvector) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); @@ -55,6 +99,15 @@ void UlxLuaCallLibrary::LuaCallAddVectorParameter(UObject *context, const FVecto sb.write_fvector(pvector); } +void UlxLuaCallLibrary::LuaCallAddVector2DParameter(UObject *context, const FVector2D &pvector) { + AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); + FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); + CheckNotEmpty(sb); + sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); + sb.write_double(pvector.X); + sb.write_double(pvector.Y); + sb.write_double(0.0); +} void UlxLuaCallLibrary::LuaCallAddBooleanParameter(UObject *context, bool pbool) { AIntegrationGameModeBase *mode = AIntegrationGameModeBase::GetFromContext(context); diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index 0c54dd32..0598371a 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -28,28 +28,34 @@ class INTEGRATION_API UlxLuaCallLibrary : public UObject public: UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallBegin(UObject *context, const FString &cname, const FString &fname); + static void LuaCallBegin(UObject *context, const FString &ClassName, const FString &FunctionName); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddStringParameter(UObject *context, const FString &pstring); + static void LuaCallAddStringParameter(UObject *context, const FString &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddNameParameter(UObject *context, const FName &pname); + static void LuaCallAddNameParameter(UObject *context, const FName &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddFloatParameter(UObject *context, double pfloat); + static void LuaCallAddFloatParameter(UObject *context, double Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddVectorParameter(UObject *context, const FVector &pvector); + static void LuaCallAddIntParameter(UObject *context, int Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallAddBooleanParameter(UObject *context, bool pbool); + static void LuaCallAddVectorParameter(UObject *context, const FVector &Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallAddVector2DParameter(UObject *context, const FVector2D &Value); + + UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") + static void LuaCallAddBooleanParameter(UObject *context, bool Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallInvoke(UObject *context, AActor *place); + static void LuaCallInvoke(UObject *context, AActor *Place); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") - static void LuaCallProbe(UObject *context, AActor *place); + static void LuaCallProbe(UObject *context, AActor *Place); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") static void InvokeEngioMove(UObject *context, const FString &action, const FVector &xyz, double facing); diff --git a/Source/Integration/LuaCallNode.cpp b/Source/Integration/LuaCallNode.cpp new file mode 100644 index 00000000..9ccffc08 --- /dev/null +++ b/Source/Integration/LuaCallNode.cpp @@ -0,0 +1,697 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + + +#include "LuaCallNode.h" + +#include "BlueprintActionDatabaseRegistrar.h" +#include "BlueprintNodeSpawner.h" +#include "Containers/EnumAsByte.h" +#include "Containers/UnrealString.h" +#include "EdGraph/EdGraph.h" +#include "EdGraph/EdGraphSchema.h" +#include "EdGraphSchema_K2.h" +#include "EdGraphSchema_K2_Actions.h" +#include "EditorCategoryUtils.h" +#include "Engine/Blueprint.h" +#include "HAL/PlatformCrt.h" +#include "Internationalization/Internationalization.h" +#include "K2Node_CallFunction.h" +#include "K2Node_MakeArray.h" +#include "K2Node_MakeStruct.h" +#include "Kismet/KismetMathLibrary.h" +#include "Kismet/KismetTextLibrary.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "Kismet2/CompilerResultsLog.h" +#include "KismetCompiler.h" +#include "LuaCall.h" +#include "Math/Vector2D.h" +#include "Misc/AssertionMacros.h" +#include "Misc/CString.h" +#include "ScopedTransaction.h" +#include "Templates/Casts.h" +#include "Templates/SubclassOf.h" +#include "UObject/Class.h" +#include "UObject/ObjectPtr.h" +#include "UObject/Package.h" +#include "UObject/UnrealNames.h" +#include "UObject/UnrealType.h" +#include "UObject/WeakObjectPtr.h" +#include "UObject/WeakObjectPtrTemplates.h" + +#define LOCTEXT_NAMESPACE "LuaCall" + +// All argument pins will have internal Names that start with "A:" + +static bool IsArgumentPin(const UEdGraphPin *Pin) { + TCHAR pname[FName::StringBufferSize]; + Pin->PinName.ToString(pname); + return pname[0] == 'A' && pname[1] == ':'; +} + +static FName ArgumentNameAddPrefix(const FString &name) { + FString Prefixed = FString("A:") + name; + return FName(*Prefixed); +} + +static FString ArgumentNameRemovePrefix(const FName &name) { + return name.ToString().Mid(2, FName::StringBufferSize); +} + +// All return value pins will have internal Names that start with "R:" + +static bool IsReturnValuePin(const UEdGraphPin *Pin) { + TCHAR pname[FName::StringBufferSize]; + Pin->PinName.ToString(pname); + return pname[0] == 'R' && pname[1] == ':'; +} + +static FName ReturnValueNameAddPrefix(const FString &name) { + FString Prefixed = FString("R:") + name; + return FName(*Prefixed); +} + +static FString ReturnValueNameRemovePrefix(const FName &name) { + return name.ToString().Mid(2, FName::StringBufferSize); +} + +// Builtin pins will have names with no prefixes. + +static const FName FunctionPinName(TEXT("Lua Function Prototype")); +static bool IsFunctionPin(const UEdGraphPin *Pin) { + return (Pin->PinName == FunctionPinName); +} + +static const FName InvokeOrProbePinName(TEXT("Invoke or Probe")); +static bool IsInvokeOrProbePin(const UEdGraphPin *Pin) { + return (Pin->PinName == InvokeOrProbePinName); +} + +static const FName PlacePinName(TEXT("Place Tangible")); +static bool IsPlacePin(const UEdGraphPin *Pin) { + return (Pin->PinName == PlacePinName); +} + +// A parser for lua function prototypes. +// +struct FlxParsedProto { + FString ErrorMessage; + TArray Tokens; + int NextToken; + FString ClassName; + FString FunctionName; + TArray Arguments; + TArray ReturnValues; + bool ExtraReturnValues; + + // Check the next token to see if it's exactly equal to text. + // + bool IsLiteral(const TCHAR *text); + + // Check the next token to see if it's an identifier. + // + bool IsIdent(); + + // Empty out the FlxParsedProto. + // + void Empty(); + + // Make a syntax error message, using the tokens. + // + void Syntax(); + + // Parse a function prototype. + // + void Parse(const FString &proto); + + // Construct with a prototype. + // + FlxParsedProto(const FString &str) { Parse(str); } +}; + +bool FlxParsedProto::IsLiteral(const TCHAR *text) { + return ((NextToken < Tokens.Num()) && (Tokens[NextToken] == text)); +} + +bool FlxParsedProto::IsIdent() { + return ((NextToken < Tokens.Num()) && (FChar::IsAlpha(Tokens[NextToken][0]))); +} + +void FlxParsedProto::Empty() { + ErrorMessage = TEXT(""); + Tokens.Empty(); + NextToken = 0; + ClassName = TEXT(""); + FunctionName = TEXT(""); + Arguments.Empty(); + ReturnValues.Empty(); + ExtraReturnValues = false; +} + +void FlxParsedProto::Syntax() { + FString Message; + if (Tokens.Num() == 0) { + Message = TEXT("Function prototype cannot be blank"); + } + for (int i = 0; i < Tokens.Num(); i++) { + if (i == NextToken) { + Message.Append(TEXT(" ? ")); + } else { + if ((i > 0) && (FChar::IsAlpha(Tokens[i][0])) && (FChar::IsAlpha(Tokens[i-1][0]))) { + Message.Append(TEXT(" ")); + } + } + Message.Append(Tokens[i]); + } + Empty(); + ErrorMessage = Message; +} + +void FlxParsedProto::Parse(const FString &str) { + Empty(); + + // Step one: tokenize. + int offset = 0; + while (offset < str.Len()) { + TCHAR c = str[offset]; + if (FChar::IsWhitespace(c)) { + offset++; + } else if (FChar::IsAlpha(c)) { + int lo = offset; + while ((offset < str.Len()) && FChar::IsAlnum(str[offset])) offset++; + Tokens.Add(str.Mid(lo, offset-lo)); + } else if (str.Mid(offset, 3) == TEXT("...")) { + Tokens.Add(str.Mid(offset, 3)); + offset += 3; + } else if (FChar::IsPunct(c)) { + Tokens.Add(str.Mid(offset, 1)); + offset += 1; + } else { + Empty(); + ErrorMessage = FString::Printf(TEXT("%s ? %s"), *str.Mid(0, offset), *str.Mid(offset)); + return; + } + } + NextToken = 0; + + // Step two: Parse. + if (!IsLiteral(TEXT("function"))) return Syntax(); + NextToken++; + if (!IsLiteral(TEXT("*")) && !IsIdent()) return Syntax(); + ClassName = Tokens[NextToken++]; + if (!IsLiteral(TEXT("."))) return Syntax(); + NextToken++; + if (!IsIdent()) return Syntax(); + FunctionName = Tokens[NextToken++]; + if (!IsLiteral(TEXT("("))) return Syntax(); + NextToken++; + if (IsIdent()) { + while (true) { + if (!IsIdent()) return Syntax(); + Arguments.Add(Tokens[NextToken++]); + if (!IsLiteral(TEXT(","))) break; + NextToken++; + } + } + if (!IsLiteral(TEXT(")"))) return Syntax(); + NextToken++; + if (IsLiteral(TEXT(":"))) { + NextToken++; + while (true) { + if (IsLiteral(TEXT("..."))) { + ExtraReturnValues = true; + NextToken++; + break; + } else if (IsIdent()) { + ReturnValues.Add(Tokens[NextToken++]); + if (!IsLiteral(TEXT(","))) break; + NextToken++; + } else { + return Syntax(); + } + } + } + if (NextToken != Tokens.Num()) return Syntax(); +} + +UK2Node_LuaCall::UK2Node_LuaCall(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + NodeTooltip = LOCTEXT("NodeTooltip", + "Probe or Invoke a Lua function.\n" + "\n" + "The lua function prototype must be a hardwired string which must look like\n" + "one of the following:" + "\n" + " function cname.fname(arg1, arg2)" + " function cname.fname(arg1, arg2) : ret1, ret2\n" + " function cname.fname(arg1, arg2) : ret1, ret2, ...\n" + "\n" + "The prototype is parsed to determine what lua function to call.\n" + "The lua call node will automatically add pins for the arguments and\n" + "return values. If the function has no return values, you can omit those\n" + "from the proto. If you put an ellipsis at the end of the return values,\n" + "then any additional return values will be collected into a\n" + "dynamically typed array of values which you can iterate over later.\n" + "\n" + "Optionally, you may use the * wildcard for the classname. In that\n" + "case, the class of the 'place' tangible will be used.\n" + "\n" + "Argument and return value pins have wildcard types initially, you can\n" + "hook them to inputs and outputs of the following types:\n" + "\n" + " string, name, float, boolean, vector\n" + "\n"); +} + +void UK2Node_LuaCall::AllocateDefaultPins() +{ + Super::AllocateDefaultPins(); + CreateCorrectPins(); +} + +void UK2Node_LuaCall::CreateCorrectPins() +{ + if (LuaFunctionPrototype.IsEmpty()) + { + LuaFunctionPrototype = TEXT("function class.func(arg1, arg2) : ret1, ret2"); + } + + if (FindPin(UEdGraphSchema_K2::PN_Execute) == nullptr) { + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute); + } + + if (FindPin(UEdGraphSchema_K2::PN_Then) == nullptr) { + CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then); + } + + if (FindPin(FunctionPinName, EGPD_Input) == nullptr) { + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, FunctionPinName); + P->DefaultValue = LuaFunctionPrototype; + } + + if (FindPin(InvokeOrProbePinName, EGPD_Input) == nullptr) { + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, StaticEnum(), InvokeOrProbePinName); + P->DefaultValue = TEXT("Probe"); + P->AutogeneratedDefaultValue = P->DefaultValue; + } + + if (FindPin(PlacePinName, EGPD_Input) == nullptr) { + UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, AActor::StaticClass(), PlacePinName); + } + + // Parse the lua function prototype. + FlxParsedProto ParsedProto(LuaFunctionPrototype); + if (!ParsedProto.ErrorMessage.IsEmpty()) + { + bHasCompilerMessage = true; + ErrorType = EMessageSeverity::Error; + ErrorMsg = FString::Printf(TEXT("Syntax error in lua function prototype: %s"), *ParsedProto.ErrorMessage); + } + + // Transfer all Existing argument and return value pins to the Old Pins Maps. + TMap OldArgumentPins; + TMap OldReturnValuePins; + for (auto It = Pins.CreateIterator(); It; ++It) + { + UEdGraphPin* CheckPin = *It; + if (IsArgumentPin(CheckPin)) { + OldArgumentPins.Add(CheckPin->PinName, CheckPin); + It.RemoveCurrent(); + } + if (IsReturnValuePin(CheckPin)) { + OldReturnValuePins.Add(CheckPin->PinName, CheckPin); + It.RemoveCurrent(); + } + } + + // Create Argument pins in the correct order, reusing old pins where possible. + for (const FString& Name : ParsedProto.Arguments) + { + FName PrefixedName = ArgumentNameAddPrefix(Name); + UEdGraphPin **OldPin = OldArgumentPins.Find(PrefixedName); + if (OldPin != nullptr) { + Pins.Emplace(*OldPin); + OldArgumentPins.Remove(PrefixedName); + } else { + CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, PrefixedName); + } + } + + // Create ReturnValue pins in the correct order, reusing old pins where possible. + for (const FString& Name : ParsedProto.ReturnValues) + { + FName PrefixedName = ReturnValueNameAddPrefix(Name); + UEdGraphPin **OldPin = OldReturnValuePins.Find(PrefixedName); + if (OldPin != nullptr) { + Pins.Emplace(*OldPin); + OldReturnValuePins.Remove(PrefixedName); + } else { + CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, PrefixedName); + } + } + + // Delete any unused pins. + for (auto &iter : OldArgumentPins) + { + iter.Value->Modify(); + iter.Value->MarkAsGarbage(); + } + for (auto &iter : OldReturnValuePins) + { + iter.Value->Modify(); + iter.Value->MarkAsGarbage(); + } + OldArgumentPins.Empty(); + OldReturnValuePins.Empty(); +} + + +void UK2Node_LuaCall::SynchronizePinType(UEdGraphPin* Pin) +{ + if (IsArgumentPin(Pin) || IsReturnValuePin(Pin)) + { + const UEdGraphSchema_K2* K2Schema = Cast(GetSchema()); + + bool bPinTypeChanged = false; + if (Pin->LinkedTo.Num() == 0) + { + static const FEdGraphPinType WildcardPinType = FEdGraphPinType(UEdGraphSchema_K2::PC_Wildcard, NAME_None, nullptr, EPinContainerType::None, false, FEdGraphTerminalType()); + + // Ensure wildcard + if (Pin->PinType != WildcardPinType) + { + Pin->PinType = WildcardPinType; + bPinTypeChanged = true; + } + } + else + { + UEdGraphPin* OtherPin = Pin->LinkedTo[0]; + + // Take the type of the connected pin + if (Pin->PinType != OtherPin->PinType) + { + Pin->PinType = OtherPin->PinType; + bPinTypeChanged = true; + } + } + + if (bPinTypeChanged) + { + // Let the graph know to refresh + GetGraph()->NotifyNodeChanged(this); + + UBlueprint* Blueprint = GetBlueprint(); + if (!Blueprint->bBeingCompiled) + { + FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); + } + } + } +} + +FText UK2Node_LuaCall::GetNodeTitle(ENodeTitleType::Type TitleType) const +{ + return LOCTEXT("LuaCall_Title", "Probe or Invoke a Lua Function"); +} + +FText UK2Node_LuaCall::GetPinDisplayName(const UEdGraphPin* Pin) const +{ + // The exec pins don't need labels. + if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) + { + return FText::GetEmpty(); + } + + // Many pins can go unlabeled if they have default values. + if (IsFunctionPin(Pin) || IsInvokeOrProbePin(Pin)) + { + if (Pin->LinkedTo.Num() == 0) + { + return FText::GetEmpty(); + } + } + + // For argument pins, we must strip off the Argument Pin Prefix. + if (IsArgumentPin(Pin)) { + return FText::FromString(ArgumentNameRemovePrefix(Pin->PinName)); + } + + // For return value pins, we must strip off the Return Value Pin Prefix. + if (IsReturnValuePin(Pin)) { + return FText::FromString(ReturnValueNameRemovePrefix(Pin->PinName)); + } + + // Otherwise, just return the Pin Name the normal way. + return FText::FromName(Pin->PinName); +} + +void UK2Node_LuaCall::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + GetGraph()->NotifyNodeChanged(this); +} + +void UK2Node_LuaCall::PinConnectionListChanged(UEdGraphPin* Pin) +{ + Modify(); + SynchronizePinType(Pin); +} + +void UK2Node_LuaCall::PinDefaultValueChanged(UEdGraphPin* Pin) +{ + if(IsFunctionPin(Pin)) + { + LuaFunctionPrototype = Pin->DefaultValue; + CreateCorrectPins(); + GetGraph()->NotifyNodeChanged(this); + } +} + +void UK2Node_LuaCall::PinTypeChanged(UEdGraphPin* Pin) +{ + SynchronizePinType(Pin); + Super::PinTypeChanged(Pin); +} + +FText UK2Node_LuaCall::GetTooltipText() const +{ + return NodeTooltip; +} + +void UK2Node_LuaCall::PostReconstructNode() +{ + Super::PostReconstructNode(); + + UEdGraph* OuterGraph = GetGraph(); + if (!IsTemplate() && OuterGraph && OuterGraph->Schema) { + for (UEdGraphPin* CurrentPin : Pins) + { + SynchronizePinType(CurrentPin); + } + } + + CreateCorrectPins(); +} + +#define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name))) + +static UFunction *GetArgumentPackingFunction(const FEdGraphPinType &Type) +{ + if (Type.PinCategory == UEdGraphSchema_K2::PC_Real) + { + return LuaCallLibraryFunction(LuaCallAddFloatParameter); + } + if (Type.PinCategory == UEdGraphSchema_K2::PC_Int) + { + return LuaCallLibraryFunction(LuaCallAddIntParameter); + } + else if (Type.PinCategory == UEdGraphSchema_K2::PC_Boolean) + { + return LuaCallLibraryFunction(LuaCallAddBooleanParameter); + } + else if (Type.PinCategory == UEdGraphSchema_K2::PC_Name) + { + return LuaCallLibraryFunction(LuaCallAddNameParameter); + } + else if (Type.PinCategory == UEdGraphSchema_K2::PC_String) + { + return LuaCallLibraryFunction(LuaCallAddStringParameter); + } + else if ((Type.PinCategory == UEdGraphSchema_K2::PC_Struct) && (Type.PinSubCategoryObject == TBaseStructure::Get())) + { + return LuaCallLibraryFunction(LuaCallAddVectorParameter); + } + else if ((Type.PinCategory == UEdGraphSchema_K2::PC_Struct) && (Type.PinSubCategoryObject == TBaseStructure::Get())) + { + return LuaCallLibraryFunction(LuaCallAddVector2DParameter); + } + else + { + return nullptr; + } +} + +void UK2Node_LuaCall::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) +{ + Super::ExpandNode(CompilerContext, SourceGraph); + const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); + + // Define a local function that creates a CallFunctionNode + auto MakeCallFunctionNode = [&](UFunction *func) + { + UK2Node_CallFunction* CallNode = CompilerContext.SpawnIntermediateNode(this, SourceGraph); + CallNode->SetFromFunction(func); + CallNode->AllocateDefaultPins(); + CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallNode, this); + return CallNode; + }; + + // The BeginNode function packs the class name and function name into the call buffer. + FlxParsedProto ParsedProto(LuaFunctionPrototype); + UK2Node_CallFunction* BeginNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallBegin)); + Schema->TrySetDefaultValue(*BeginNode->FindPinChecked(TEXT("ClassName")), ParsedProto.ClassName); + Schema->TrySetDefaultValue(*BeginNode->FindPinChecked(TEXT("FunctionName")), ParsedProto.FunctionName); + UK2Node_CallFunction* PrevNode = BeginNode; + + // Add Packing operations for all argument pins. + for (UEdGraphPin* Pin : Pins) + { + if (IsArgumentPin(Pin)) + { + UFunction *PackingFunc = GetArgumentPackingFunction(Pin->PinType); + if (PackingFunc != nullptr) + { + UK2Node_CallFunction *PackNode = MakeCallFunctionNode(PackingFunc); + CompilerContext.MovePinLinksToIntermediate(*Pin, *PackNode->FindPinChecked(TEXT("Value"))); + PrevNode->GetThenPin()->MakeLinkTo(PackNode->GetExecPin()); + PrevNode = PackNode; + } + else + { + FText PinName = GetPinDisplayName(Pin); + FText PinType = FText::FromName(Pin->PinType.PinCategory); + FText Error = FText::Format(LOCTEXT("Error_UnexpectedPinType", "Pin '{0}' has an unexpected type: {1}"), PinName, PinType); + CompilerContext.MessageLog.Error(*Error.ToString()); + } + } + } + + // Add the invoke or probe node. + bool IsInvoke = (FindPin(InvokeOrProbePinName, EGPD_Input)->DefaultValue == TEXT("Invoke")); + UFunction *Action = IsInvoke ? LuaCallLibraryFunction(LuaCallInvoke) : LuaCallLibraryFunction(LuaCallProbe); + UK2Node_CallFunction* ActionNode = MakeCallFunctionNode(Action); + CompilerContext.MovePinLinksToIntermediate(*FindPin(PlacePinName, EGPD_Input), *ActionNode->FindPinChecked(TEXT("Place"))); + PrevNode->GetThenPin()->MakeLinkTo(ActionNode->GetExecPin()); + + // Link up the Exec pins. + CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin()); + CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *ActionNode->GetThenPin()); + + BreakAllNodeLinks(); +} + + +UK2Node::ERedirectType UK2Node_LuaCall::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const +{ + ERedirectType RedirectType = ERedirectType_None; + + // if the pin names do match + if (NewPin->PinName.ToString().Equals(OldPin->PinName.ToString(), ESearchCase::CaseSensitive)) + { + // Make sure we're not dealing with a menu node + UEdGraph* OuterGraph = GetGraph(); + if( OuterGraph && OuterGraph->Schema ) + { + const UEdGraphSchema_K2* K2Schema = Cast(GetSchema()); + if( !K2Schema || K2Schema->IsSelfPin(*NewPin) || K2Schema->ArePinTypesCompatible(OldPin->PinType, NewPin->PinType) ) + { + RedirectType = ERedirectType_Name; + } + else + { + RedirectType = ERedirectType_None; + } + } + } + else + { + // try looking for a redirect if it's a K2 node + if (UK2Node* Node = Cast(NewPin->GetOwningNode())) + { + // if you don't have matching pin, now check if there is any redirect param set + TArray OldPinNames; + GetRedirectPinNames(*OldPin, OldPinNames); + + FName NewPinName; + RedirectType = ShouldRedirectParam(OldPinNames, /*out*/ NewPinName, Node); + + // make sure they match + if ((RedirectType != ERedirectType_None) && (!NewPin->PinName.ToString().Equals(NewPinName.ToString(), ESearchCase::CaseSensitive))) + { + RedirectType = ERedirectType_None; + } + } + } + + return RedirectType; +} + +bool UK2Node_LuaCall::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const +{ + // The function pin cannot be connected. + if (IsFunctionPin(MyPin)) + { + OutReason = LOCTEXT("Error_FunctionPrototypeMustBeHardwired", "Lua function prototype must be a hardwired constant.").ToString(); + return true; + } + + // The invoke-or-probe pin cannot be connected. + if (IsInvokeOrProbePin(MyPin)) + { + OutReason = LOCTEXT("Error_InvokeOrProbeMustBeHardwired", "Invoke vs Probe must be a hardwired constant.").ToString(); + return true; + } + + // Argument input pins may only be connected to packable types. + if (IsArgumentPin(MyPin)) + { + UFunction *Packer = GetArgumentPackingFunction(OtherPin->PinType); + if (Packer == nullptr) + { + OutReason = LOCTEXT("Error_InvalidArgumentType", "Lua Call Arguments may be float, boolean, string, name, or vector.").ToString(); + return true; + } + } + + return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason); +} + + +void UK2Node_LuaCall::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const +{ + // actions get registered under specific object-keys; the idea is that + // actions might have to be updated (or deleted) if their object-key is + // mutated (or removed)... here we use the node's class (so if the node + // type disappears, then the action should go with it) + UClass* ActionKey = GetClass(); + // to keep from needlessly instantiating a UBlueprintNodeSpawner, first + // check to make sure that the registrar is looking for actions of this type + // (could be regenerating actions for a specific asset, and therefore the + // registrar would only accept actions corresponding to that asset) + if (ActionRegistrar.IsOpenForRegistration(ActionKey)) + { + UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); + check(NodeSpawner != nullptr); + + ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); + } +} + +FText UK2Node_LuaCall::GetMenuCategory() const +{ + return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Text); +} + + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Integration/LuaCallNode.h b/Source/Integration/LuaCallNode.h new file mode 100644 index 00000000..a53b0fa7 --- /dev/null +++ b/Source/Integration/LuaCallNode.h @@ -0,0 +1,87 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "BlueprintErrors.h" +#include "Containers/Array.h" +#include "CoreMinimal.h" +#include "EdGraph/EdGraphNode.h" +#include "EdGraph/EdGraphPin.h" +#include "HAL/Platform.h" +#include "Internationalization/Text.h" +#include "K2Node.h" +#include "UObject/NameTypes.h" +#include "UObject/ObjectMacros.h" +#include "UObject/UObjectGlobals.h" +#include "BlueprintErrors.h" + +#include "LuaCallNode.generated.h" + +class FBlueprintActionDatabaseRegistrar; +class FString; +class UEdGraph; +class UObject; + +UENUM(BlueprintType) +enum class ElxInvokeOrProbe : uint8 { + + /* Invoke the lua function: call it on the server, mutating the world state. */ + Invoke, + + /* Probe the lua function: call it locally, not mutating the world state. */ + Probe, +}; + + + +// +// The Lua Call K2Node. +// +UCLASS(MinimalAPI) +class UK2Node_LuaCall : public UK2Node +{ + GENERATED_UCLASS_BODY() + + //~ Begin UObject Interface + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; + //~ End UObject Interface + + //~ Begin UEdGraphNode Interface. + virtual void AllocateDefaultPins() override; + virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; + virtual bool ShouldShowNodeProperties() const override { return true; } + virtual void PinConnectionListChanged(UEdGraphPin* Pin) override; + virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override; + virtual void PinTypeChanged(UEdGraphPin* Pin) override; + virtual FText GetTooltipText() const override; + virtual FText GetPinDisplayName(const UEdGraphPin* Pin) const override; + //~ End UEdGraphNode Interface. + + //~ Begin UK2Node Interface. + virtual bool IsNodePure() const override { return false; } + virtual void PostReconstructNode() override; + virtual bool NodeCausesStructuralBlueprintChange() const override { return true; } + virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override; + virtual ERedirectType DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const override; + virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override; + virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override; + virtual FText GetMenuCategory() const override; + virtual int32 GetNodeRefreshPriority() const override { return EBaseNodeRefreshPriority::Low_UsesDependentWildcard; } + //~ End UK2Node Interface. + +private: + /** Create all necessary pins. */ + void CreateCorrectPins(); + + /** Synchronize the type of the given pin with the type its connected to, or reset it to a wildcard pin if there's no connection */ + void SynchronizePinType(UEdGraphPin* Pin); + +private: + /** The lua function prototype, which must be saved as a property **/ + UPROPERTY() + FString LuaFunctionPrototype; + + /** Tooltip text for this node. */ + FText NodeTooltip; +}; + diff --git a/build-everything.py b/build-everything.py index 4f39c396..a8b62497 100755 --- a/build-everything.py +++ b/build-everything.py @@ -140,109 +140,154 @@ shell(UNREALENGINE, f"{UNREALENGINE}/Setup.{BAT}") # # Use UnrealBuildTool to generate a rough draft of Integration.code-workspace. +# We're not going to use it, but we set it aside as a reference that you can +# study to make changes to this script. # +Path(f"{INTEGRATION}/Integration.code-workspace").unlink(missing_ok=True) +Path(f"{INTEGRATION}/Integration.code-workspace.old").unlink(missing_ok=True) shell(INTEGRATION, f'{UNREALENGINE}/GenerateProjectFiles.{BAT} -projectfiles -project="{INTEGRATION}/Integration.uproject" -game') +Path(f"{INTEGRATION}/Integration.code-workspace").rename(f"{INTEGRATION}/Integration.code-workspace.old") # -# Load the rough Integration.code-workspace into RAM, then delete the rough draft. +# Create a trivial makefile that calls into the unreal build system. # -with open(f"{INTEGRATION}/Integration.code-workspace") as original: - WORKSPACE=json.load(original) -Path(f"{INTEGRATION}/Integration.code-workspace").unlink() +writefile(f"{INTEGRATION}/Makefile", f""" +# This makefile just invokes the unreal build system, then the luprex build system. + +all: +\t{UNREALENGINE}/Engine/Build/BatchFiles/Linux/Build.sh IntegrationEditor Linux DebugGame {INTEGRATION}/Integration.uproject -waitmutex +\t(cd luprex ; make all) + +clean: +\t{UNREALENGINE}/Engine/Build/BatchFiles/Linux/Build.sh IntegrationEditor Linux DebugGame {INTEGRATION}/Integration.uproject -waitmutex -clean +\t(cd luprex ; make clean) +""") # -# Configure the correct build task as the default task. +# Build our own Integration.code-workspace from scratch. # -for task in WORKSPACE["tasks"]["tasks"]: - if task["label"] == f"IntegrationEditor {OS} DebugGame Build": - task["group"] = { "kind": "build", "isDefault": True } +WORKSPACE={} -# -# Delete all build tasks that aren't relevant. -# +WORKSPACE["folders"] = [] +WORKSPACE["folders"].append({ "name": "Integration", "path": "." }) +WORKSPACE["folders"].append({ "name": "UE5", "path": UNREALENGINE }) -def goodtask(task): - return task["label"].startswith(f"IntegrationEditor {OS} DebugGame") +WORKSPACE["settings"] = {} +WORKSPACE["settings"]["typescript.tsc.autoDetect"] = "off" +WORKSPACE["settings"]["lldb.dereferencePointers"] = False +WORKSPACE["settings"]["npm.autoDetect"] = "off" -WORKSPACE["tasks"]["tasks"] = [x for x in WORKSPACE["tasks"]["tasks"] if goodtask(x)] - -# -# Add a build task for Luprex -# - -LUPREXBUILDTASK={} -WORKSPACE["tasks"]["tasks"].append(LUPREXBUILDTASK) -LUPREXBUILDTASK["label"] = "Build Luprex" -LUPREXBUILDTASK["group"] = "build" -LUPREXBUILDTASK["command"] = "make" -LUPREXBUILDTASK["problemMatcher"] = "$msCompile" -LUPREXBUILDTASK["type"] = "shell" -LUPREXBUILDTASK["options"] = {} -LUPREXBUILDTASK["options"]["cwd"] = f"{INTEGRATION}/luprex" - -# -# Add a presentation { clear=true } to all build tasks. -# - -for task in WORKSPACE["tasks"]["tasks"]: - task["presentation"] = {} - task["presentation"]["clear"] = True - -# -# Convert all launch configurations to lldb. -# - -LLDBINIT=[ - f'command script import {UNREALENGINE}/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py', - f'settings set target.inline-breakpoint-strategy always', - f'target stop-hook add --one-liner "p ::UngrabAllInputImpl()"', -] - -for config in WORKSPACE["launch"]["configurations"]: - config["type"] = "lldb" - config["initCommands"] = LLDBINIT - config["args"] = [ f"{INTEGRATION}/Integration.uproject", f"-userdir=User/{USER}" ] - config.pop("visualizerFile", None) - config.pop("showDisplayString", None) - -# -# Delete all but the relevant launch configuration. -# - -def goodconf(config): - return config["name"] == "Launch IntegrationEditor (DebugGame)" - -WORKSPACE["launch"]["configurations"] = [x for x in WORKSPACE["launch"]["configurations"] if goodconf(x)] - -# -# Add some recommended extensions. -# - -EXTENSIONS=set(WORKSPACE["extensions"]["recommendations"]) -EXTENSIONS.add("ms-python.python") -EXTENSIONS.add("vadimcn.vscode-lldb") -WORKSPACE["extensions"]["recommendations"] = list(EXTENSIONS) - -# -# Tell vscode not to try watching all the UnrealEngine source code for modifications. -# Attempting this overruns a Linux hardwired limit on file watches. -# - -WORKSPACE["settings"]["files.watcherExclude"] = { - f'{UNREALENGINE}/Engine/**' : True, - f'{UNREALENGINE}/Samples/**' : True, - f'{UNREALENGINE}/Templates/**' : True +WORKSPACE["settings"]["files.watcherExclude"] = {} +WORKSPACE["settings"]["files.watcherExclude"]["/home/jyelon/UnrealEngine/Engine/**"] = True +WORKSPACE["settings"]["files.watcherExclude"]["/home/jyelon/UnrealEngine/Samples/**"] = True +WORKSPACE["settings"]["files.watcherExclude"]["/home/jyelon/UnrealEngine/Templates/**"] = True +WORKSPACE["settings"]["files.associations"] = { + "*.ipp": "cpp", + "locale": "cpp", + "random": "cpp", + "queue": "cpp", + "stack": "cpp", + "__locale": "cpp", + "functional": "cpp", + "sstream": "cpp", + "regex": "cpp", + "*.inc": "cpp", + "strstream": "cpp", + "string_view": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "scoped_allocator": "cpp", + "array": "cpp", + "hash_map": "cpp", + "hash_set": "cpp", + "bitset": "cpp", + "slist": "cpp", + "initializer_list": "cpp", + "valarray": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "span": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "ranges": "cpp", + "utility": "cpp", + "ratio": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "__bit_reference": "cpp", + "__node_handle": "cpp", + "atomic": "cpp", + "__memory": "cpp", + "limits": "cpp", + "optional": "cpp", + "variant": "cpp" } -# -# Tell the LLDB plugin not to dereference pointers. This is dumb behavior, -# it dereferences "char *" and shows only the first character. Not dereferencing -# the pointer shows the whole string. -# -WORKSPACE["settings"]["lldb.dereferencePointers"] = False +WORKSPACE["extensions"] = {} +WORKSPACE["extensions"]["recommendations"] = [ + "vadimcn.vscode-lldb", + "dfarley1.file-picker", + "ms-python.python", + "ms-vscode.cpptools", + "ms-dotnettools.csharp", + "ms-vscode.mono-debug" +] + + +WORKSPACE["tasks"] = {} +WORKSPACE["tasks"]["version"] = "2.0.0" +WORKSPACE["tasks"]["tasks"] = [] +WORKSPACE["tasks"]["tasks"].append({ + "label": "Make All", + "group": { "kind": "build", "isDefault": True }, + "command": "make all", + "presentation" : { "clear" : True }, + "problemMatcher": "$msCompile", + "type": "shell", + }) + +WORKSPACE["tasks"]["tasks"].append({ + "label": "Make Clean", + "group": "build", + "command": "make clean", + "presentation" : { "clear" : True }, + "problemMatcher": "$msCompile", + "type": "shell", + }) + + +WORKSPACE["launch"] = {} +WORKSPACE["launch"]["version"] = "0.2.0" +WORKSPACE["launch"]["configurations"] = [] +WORKSPACE["launch"]["configurations"].append({ + "name": "Launch Editor with Luprex", + "request": "launch", + "program": f"{UNREALENGINE}/Engine/Binaries/Linux/UnrealEditor-Linux-DebugGame", + "preLaunchTask": "Make All", + "args": [ + f"{INTEGRATION}/Integration.uproject", + "-userdir=User/jyelon" + ], + "cwd": UNREALENGINE, + "type": "lldb", + "initCommands": [ + f"command script import {UNREALENGINE}/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py", + "settings set target.inline-breakpoint-strategy always", + "target stop-hook add --one-liner \"p ::UngrabAllInputImpl()\"" + ] + }) + # # Write Integration.code-workspace. diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index fd9d42a5..65b25c23 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -19,7 +19,11 @@ void push_simple_dynamic(lua_State *L, StreamBuffer *sb) { break; } case SimpleDynamicTag::TOKEN: { - LuaToken token(sb->read_string_view()); + std::string_view toktext = sb->read_string_view(); + LuaToken token(toktext); + if (token.empty() && !toktext.empty()) { + throw StreamCorruption(); + } lua_pushlightuserdata(L, token.voidvalue()); break; } @@ -860,6 +864,7 @@ void World::invoke_lua_expr(int64_t actor_id, int64_t place_id, std::string_view assert(stack_is_clear()); } +volatile int vx; void World::invoke_lua_call(int64_t actor_id, int64_t place_id, std::string_view datapack) { assert(stack_is_clear()); @@ -879,8 +884,12 @@ void World::invoke_lua_call(int64_t actor_id, int64_t place_id, std::string_view if ((!sv::is_lua_id(classname)) || (!sv::is_lua_id(funcname))) { return; } + // TODO: Add support for the wildcard classname. // TODO: Add check for permit_invoke(classname, funcname) + if (funcname == "printhi") { + vx = 0; + } { lua_State *L = state(); LuaVar lclass, lfunc; diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index 2076d01b..8196bdaf 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -22,3 +22,7 @@ function engio.move(actor, place, action, xyz, facing) dprint("engio.move ", action, " ", xyz[1], " ", xyz[2], " ", xyz[3]) tangible.animate(actor, nil, {action=action, xyz=xyz, facing=facing}) end + +function engio.printhi(a1, a2, a3, a4, a5) + pprint("Hi there", a1, a2, a3, a4, a5) +end