2025-02-24 16:46:05 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "LuaCallNode.h"
|
2026-02-25 14:48:14 -05:00
|
|
|
#include "StreamBuffer.h"
|
2025-02-24 16:46:05 -05:00
|
|
|
|
2026-02-16 17:24:52 -05:00
|
|
|
#include "GameFramework/Actor.h"
|
2025-02-24 16:46:05 -05:00
|
|
|
#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"
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
#define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name)))
|
2025-02-24 16:46:05 -05:00
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
// All argument pins will have internal Names that start with "A:"
|
2025-02-24 16:46:05 -05:00
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
const FName UK2Node_LuaInvoke::FunctionPinName(TEXT("Lua Function Prototype"));
|
|
|
|
|
const FName UK2Node_LuaInvoke::PlacePinName(TEXT("Place Tangible"));
|
|
|
|
|
const FName UK2Node_LuaInvoke::ExtraResultsPinName(TEXT("Extra Results"));
|
2025-04-08 18:53:26 -04:00
|
|
|
const FName UK2Node_LuaInvoke::ErrorPinName(TEXT("Error"));
|
2025-02-24 16:46:05 -05:00
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
bool UK2Node_LuaInvoke::IsPrefix(const UEdGraphPin *Pin, char Prefix)
|
2025-03-28 23:31:44 -04:00
|
|
|
{
|
2025-02-24 16:46:05 -05:00
|
|
|
TCHAR pname[FName::StringBufferSize];
|
|
|
|
|
Pin->PinName.ToString(pname);
|
2025-03-28 23:31:44 -04:00
|
|
|
return pname[0] == Prefix && pname[1] == ':';
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
FName UK2Node_LuaInvoke::AddPrefix(const FString &Name, char Prefix)
|
2025-03-28 23:31:44 -04:00
|
|
|
{
|
|
|
|
|
TCHAR PrefixChars[3];
|
|
|
|
|
PrefixChars[0] = Prefix;
|
|
|
|
|
PrefixChars[1] = ':';
|
|
|
|
|
PrefixChars[2] = 0;
|
|
|
|
|
FString Prefixed = PrefixChars + Name;
|
2025-02-24 16:46:05 -05:00
|
|
|
return FName(*Prefixed);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
FString UK2Node_LuaInvoke::RemovePrefix(FName Name, char Prefix)
|
2025-03-28 23:31:44 -04:00
|
|
|
{
|
|
|
|
|
TCHAR pname[FName::StringBufferSize];
|
|
|
|
|
Name.ToString(pname);
|
|
|
|
|
check(pname[0] == Prefix);
|
|
|
|
|
check(pname[1] == ':');
|
|
|
|
|
return FString(pname + 2);
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
2025-02-26 14:47:53 -05:00
|
|
|
static FEdGraphPinType GetPinType(const FProperty *Property)
|
|
|
|
|
{
|
|
|
|
|
FEdGraphPinType PinType;
|
|
|
|
|
bool IsWeak;
|
|
|
|
|
FName PinCat, PinSubCat;
|
|
|
|
|
UObject *PinSubCatObj = nullptr;
|
|
|
|
|
UEdGraphSchema_K2::GetPropertyCategoryInfo(Property, PinCat, PinSubCat, PinSubCatObj, IsWeak);
|
|
|
|
|
PinType.PinCategory = PinCat;
|
|
|
|
|
PinType.PinSubCategory = PinSubCat;
|
|
|
|
|
PinType.PinSubCategoryObject = PinSubCatObj;
|
|
|
|
|
return PinType;
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
UK2Node_LuaInvoke::UK2Node_LuaInvoke(const FObjectInitializer& ObjectInitializer)
|
|
|
|
|
: Super(ObjectInitializer), NodeTooltip(MakeTooltip())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UK2Node_LuaProbe::UK2Node_LuaProbe(const FObjectInitializer& ObjectInitializer)
|
2025-02-24 16:46:05 -05:00
|
|
|
: Super(ObjectInitializer)
|
|
|
|
|
{
|
2025-04-08 16:31:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FText UK2Node_LuaInvoke::MakeTooltip() const
|
|
|
|
|
{
|
|
|
|
|
// TODO: replace this string manipulation with text manipulation.
|
2025-02-26 14:47:53 -05:00
|
|
|
FString ArgTypes = UlxLuaCallLibrary::AllKnownArgumentTypes();
|
|
|
|
|
FString RetTypes = UlxLuaCallLibrary::AllKnownReturnValueTypes();
|
2025-04-08 16:31:16 -04:00
|
|
|
return FText::FromString(FString::Printf(TEXT(
|
2025-02-26 14:47:53 -05:00
|
|
|
"Call a Lua function.\n"
|
2025-02-24 16:46:05 -05:00
|
|
|
"\n"
|
|
|
|
|
"The lua function prototype must be a hardwired string which must look like\n"
|
2025-02-26 14:47:53 -05:00
|
|
|
"one of the following:\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" classname.funcname(int arg1, int arg2)\n"
|
|
|
|
|
" classname.funcname(int arg1, int arg2) : int ret1, int ret2\n"
|
|
|
|
|
" classname.funcname(int arg1, int arg2) : int ret1, int ret2, ...\n"
|
2025-02-24 16:46:05 -05:00
|
|
|
"\n"
|
2025-02-26 14:47:53 -05:00
|
|
|
"You must specify types for the argument and return value pins. The:\n"
|
|
|
|
|
"types that you may specify are:\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"Arguments: %s\n"
|
|
|
|
|
"Return Values: %s\n"
|
2025-02-24 16:46:05 -05:00
|
|
|
"\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"
|
2025-02-26 14:47:53 -05:00
|
|
|
"\n"), *ArgTypes, *RetTypes));
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::AllocateDefaultPins()
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
Super::AllocateDefaultPins();
|
|
|
|
|
CreateCorrectPins();
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::CreateCorrectPins()
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
if (LuaFunctionPrototype.IsEmpty())
|
|
|
|
|
{
|
2025-02-26 14:47:53 -05:00
|
|
|
LuaFunctionPrototype = TEXT("class.func(int arg1, int arg2) : int ret1, int ret2");
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
// 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 pins to the old pins map and clear the pin list.
|
|
|
|
|
TMap<FName, UEdGraphPin *> OldPins;
|
|
|
|
|
for (auto It = Pins.CreateIterator(); It; ++It)
|
|
|
|
|
{
|
|
|
|
|
UEdGraphPin* CheckPin = *It;
|
|
|
|
|
OldPins.Add(CheckPin->PinName, CheckPin);
|
|
|
|
|
}
|
|
|
|
|
Pins.Empty();
|
|
|
|
|
|
|
|
|
|
// KeepPin is a function that moves a pin from the old pins
|
|
|
|
|
// map back onto the pins list.
|
|
|
|
|
auto KeepPin = [&](FName Name, FEdGraphPinType Type = FEdGraphPinType()) -> bool
|
|
|
|
|
{
|
|
|
|
|
UEdGraphPin **OldPin = OldPins.Find(Name);
|
|
|
|
|
if ((OldPin != nullptr) && (((*OldPin)->PinType == Type) || (Type.PinCategory == FName())))
|
|
|
|
|
{
|
|
|
|
|
Pins.Emplace(*OldPin);
|
|
|
|
|
OldPins.Remove(Name);
|
2025-04-08 18:53:26 -04:00
|
|
|
return true;
|
2025-03-28 23:31:44 -04:00
|
|
|
}
|
2025-04-08 18:53:26 -04:00
|
|
|
return false;
|
2025-03-28 23:31:44 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!KeepPin(UEdGraphSchema_K2::PN_Execute))
|
|
|
|
|
{
|
2025-02-24 16:46:05 -05:00
|
|
|
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
if (!KeepPin(UEdGraphSchema_K2::PN_Then))
|
|
|
|
|
{
|
2025-02-24 16:46:05 -05:00
|
|
|
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
if (!IsInvoke())
|
2025-04-07 18:00:45 -04:00
|
|
|
{
|
|
|
|
|
if (!KeepPin(ErrorPinName))
|
|
|
|
|
{
|
|
|
|
|
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, ErrorPinName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
if (!KeepPin(FunctionPinName))
|
|
|
|
|
{
|
2025-02-24 16:46:05 -05:00
|
|
|
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, FunctionPinName);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
if (!KeepPin(PlacePinName))
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-28 23:31:44 -04:00
|
|
|
UEdGraphPin *P = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, AActor::StaticClass(), PlacePinName);
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
2025-04-07 18:00:45 -04:00
|
|
|
// Copy the property names into the pins.
|
|
|
|
|
UEdGraphPin *FunctionPin = FindPinChecked(FunctionPinName);
|
|
|
|
|
FunctionPin->DefaultValue = LuaFunctionPrototype;
|
|
|
|
|
|
2025-02-24 16:46:05 -05:00
|
|
|
// Create Argument pins in the correct order, reusing old pins where possible.
|
2025-02-26 14:47:53 -05:00
|
|
|
for (const FlxParsedProto::Pin & Pin : ParsedProto.Arguments)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-28 23:31:44 -04:00
|
|
|
FName PrefixedName = AddPrefix(Pin.Name, 'A');
|
2025-02-26 14:47:53 -05:00
|
|
|
UFunction *Accessor = UlxLuaCallLibrary::GetArgumentPacker(Pin.Type);
|
|
|
|
|
if (Accessor == nullptr) {
|
|
|
|
|
bHasCompilerMessage = true;
|
|
|
|
|
ErrorType = EMessageSeverity::Error;
|
|
|
|
|
ErrorMsg = FString::Printf(TEXT("Unknown argument type: %s"), *Pin.Type);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Value")));
|
2025-03-28 23:31:44 -04:00
|
|
|
if (!KeepPin(PrefixedName, PinType))
|
|
|
|
|
{
|
2025-02-26 14:47:53 -05:00
|
|
|
CreatePin(EGPD_Input, PinType, PrefixedName);
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create ReturnValue pins in the correct order, reusing old pins where possible.
|
2025-02-26 14:47:53 -05:00
|
|
|
for (const FlxParsedProto::Pin & Pin : ParsedProto.ReturnValues)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-28 23:31:44 -04:00
|
|
|
FName PrefixedName = AddPrefix(Pin.Name, 'R');
|
2025-02-26 14:47:53 -05:00
|
|
|
UFunction *Accessor = UlxLuaCallLibrary::GetReturnValueUnpacker(Pin.Type);
|
|
|
|
|
if (Accessor == nullptr) {
|
|
|
|
|
bHasCompilerMessage = true;
|
|
|
|
|
ErrorType = EMessageSeverity::Error;
|
|
|
|
|
ErrorMsg = FString::Printf(TEXT("Unknown return value type: %s"), *Pin.Type);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-04-08 18:53:26 -04:00
|
|
|
FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Result")));
|
2025-03-28 23:31:44 -04:00
|
|
|
if (!KeepPin(PrefixedName, PinType))
|
|
|
|
|
{
|
2025-02-26 14:47:53 -05:00
|
|
|
CreatePin(EGPD_Output, PinType, PrefixedName);
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
if (ParsedProto.ExtraReturnValues)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-28 23:31:44 -04:00
|
|
|
if (!KeepPin(ExtraResultsPinName))
|
|
|
|
|
{
|
|
|
|
|
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, UlxLuaValues::StaticClass(), ExtraResultsPinName);
|
|
|
|
|
}
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
2025-03-28 23:31:44 -04:00
|
|
|
|
|
|
|
|
// Delete any unused pins.
|
|
|
|
|
for (auto &iter : OldPins)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
iter.Value->Modify();
|
|
|
|
|
iter.Value->MarkAsGarbage();
|
|
|
|
|
}
|
2025-03-28 23:31:44 -04:00
|
|
|
OldPins.Empty();
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
FText UK2Node_LuaInvoke::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-04-08 16:31:16 -04:00
|
|
|
if (IsInvoke())
|
|
|
|
|
{
|
|
|
|
|
return LOCTEXT("LuaInvoke_Title", "Invoke a Lua Function");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return LOCTEXT("LuaProbe_Title", "Probe a Lua Function");
|
|
|
|
|
}
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
FText UK2Node_LuaInvoke::GetPinDisplayName(const UEdGraphPin* Pin) const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
// The exec pins don't need labels.
|
2025-04-07 18:00:45 -04:00
|
|
|
if ((Pin->PinName == UEdGraphSchema_K2::PN_Execute) || (Pin->PinName == UEdGraphSchema_K2::PN_Then))
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
return FText::GetEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Many pins can go unlabeled if they have default values.
|
2025-04-08 16:31:16 -04:00
|
|
|
if (Pin->PinName == FunctionPinName)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
if (Pin->LinkedTo.Num() == 0)
|
|
|
|
|
{
|
|
|
|
|
return FText::GetEmpty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For argument pins, we must strip off the Argument Pin Prefix.
|
2025-03-28 23:31:44 -04:00
|
|
|
if (IsPrefix(Pin, 'A'))
|
|
|
|
|
{
|
|
|
|
|
return FText::FromString(RemovePrefix(Pin->PinName, 'A'));
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For return value pins, we must strip off the Return Value Pin Prefix.
|
2025-03-28 23:31:44 -04:00
|
|
|
if (IsPrefix(Pin, 'R'))
|
|
|
|
|
{
|
|
|
|
|
return FText::FromString(RemovePrefix(Pin->PinName, 'R'));
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, just return the Pin Name the normal way.
|
|
|
|
|
return FText::FromName(Pin->PinName);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
|
GetGraph()->NotifyNodeChanged(this);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::PinConnectionListChanged(UEdGraphPin* Pin)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
Modify();
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::PinDefaultValueChanged(UEdGraphPin* Pin)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-28 23:31:44 -04:00
|
|
|
if(Pin->PinName == FunctionPinName)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
LuaFunctionPrototype = Pin->DefaultValue;
|
|
|
|
|
CreateCorrectPins();
|
|
|
|
|
GetGraph()->NotifyNodeChanged(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
FText UK2Node_LuaInvoke::GetTooltipText() const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
return NodeTooltip;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::PostReconstructNode()
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
Super::PostReconstructNode();
|
|
|
|
|
CreateCorrectPins();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define LuaCallLibraryFunction(name) (UlxLuaCallLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxLuaCallLibrary, name)))
|
|
|
|
|
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
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<UK2Node_CallFunction>(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);
|
2025-04-07 18:00:45 -04:00
|
|
|
UEdGraphPin *ThenPin = BeginNode->GetThenPin();
|
|
|
|
|
|
2025-02-24 16:46:05 -05:00
|
|
|
// Add Packing operations for all argument pins.
|
2025-02-26 14:47:53 -05:00
|
|
|
for (const FlxParsedProto::Pin &PinInfo : ParsedProto.Arguments)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-28 23:31:44 -04:00
|
|
|
UEdGraphPin *Pin = FindPinChecked(AddPrefix(PinInfo.Name, 'A'));
|
2025-02-26 14:47:53 -05:00
|
|
|
UFunction *PackingFunc = UlxLuaCallLibrary::GetArgumentPacker(PinInfo.Type);
|
|
|
|
|
if (PackingFunc == nullptr)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-02-26 14:47:53 -05:00
|
|
|
// This codepath should be unreachable, but just in case.
|
|
|
|
|
CompilerContext.MessageLog.Error(TEXT("All argument pins must have known types"));
|
|
|
|
|
continue;
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
2025-02-26 14:47:53 -05:00
|
|
|
UK2Node_CallFunction *PackNode = MakeCallFunctionNode(PackingFunc);
|
|
|
|
|
CompilerContext.MovePinLinksToIntermediate(*Pin, *PackNode->FindPinChecked(TEXT("Value")));
|
2025-04-07 18:00:45 -04:00
|
|
|
ThenPin->MakeLinkTo(PackNode->GetExecPin());
|
|
|
|
|
ThenPin = PackNode->GetThenPin();
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the invoke or probe node.
|
2025-04-08 18:53:26 -04:00
|
|
|
UEdGraphPin *ReturnArrayPin = nullptr;
|
2025-04-08 16:31:16 -04:00
|
|
|
if (IsInvoke())
|
2025-04-07 18:00:45 -04:00
|
|
|
{
|
2025-04-08 16:31:16 -04:00
|
|
|
UK2Node_CallFunction* ActionNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallInvoke));
|
2025-04-08 18:53:26 -04:00
|
|
|
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PlacePinName), *ActionNode->FindPinChecked(TEXT("Place")));
|
2025-04-07 18:00:45 -04:00
|
|
|
ThenPin->MakeLinkTo(ActionNode->GetExecPin());
|
2025-04-08 16:31:16 -04:00
|
|
|
ThenPin = ActionNode->GetThenPin();
|
2025-04-07 18:00:45 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-04-08 16:31:16 -04:00
|
|
|
UK2Node_CallFunction* ActionNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallProbe));
|
2025-04-08 18:53:26 -04:00
|
|
|
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PlacePinName), *ActionNode->FindPinChecked(TEXT("Place")));
|
|
|
|
|
CompilerContext.CopyPinLinksToIntermediate(*FindPinChecked(ErrorPinName), *ActionNode->FindPinChecked(TEXT("False")));
|
|
|
|
|
ReturnArrayPin = ActionNode->FindPinChecked(TEXT("ReturnArray"));
|
2025-04-07 18:00:45 -04:00
|
|
|
ThenPin->MakeLinkTo(ActionNode->GetExecPin());
|
2025-04-08 16:31:16 -04:00
|
|
|
ThenPin = ActionNode->FindPinChecked(TEXT("True"));
|
2025-04-07 18:00:45 -04:00
|
|
|
}
|
2025-02-26 14:47:53 -05:00
|
|
|
|
2025-04-08 18:53:26 -04:00
|
|
|
if (IsInvoke())
|
2025-02-26 14:47:53 -05:00
|
|
|
{
|
2025-04-08 18:53:26 -04:00
|
|
|
// Make sure we didn't have return values for an invoke.
|
|
|
|
|
if (IsInvoke() && ((ParsedProto.ReturnValues.Num() > 0) || (ParsedProto.ExtraReturnValues)))
|
2025-02-26 14:47:53 -05:00
|
|
|
{
|
2025-04-08 18:53:26 -04:00
|
|
|
CompilerContext.MessageLog.Error(TEXT("Lua Call in Invoke mode does not support return values"));
|
2025-02-26 14:47:53 -05:00
|
|
|
}
|
|
|
|
|
}
|
2025-04-08 18:53:26 -04:00
|
|
|
else
|
2025-02-26 14:47:53 -05:00
|
|
|
{
|
2025-04-08 18:53:26 -04:00
|
|
|
// Add Unpacking operations for all return value pins.
|
|
|
|
|
for (const FlxParsedProto::Pin &PinInfo : ParsedProto.ReturnValues)
|
|
|
|
|
{
|
|
|
|
|
UEdGraphPin *Pin = FindPinChecked(AddPrefix(PinInfo.Name, 'R'));
|
|
|
|
|
UFunction *UnpackingFunc = UlxLuaCallLibrary::GetReturnValueUnpacker(PinInfo.Type);
|
|
|
|
|
if (UnpackingFunc == nullptr)
|
|
|
|
|
{
|
|
|
|
|
// This codepath should be unreachable, but just in case.
|
|
|
|
|
CompilerContext.MessageLog.Error(TEXT("All return value pins must have known types."));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(UnpackingFunc);
|
|
|
|
|
ReturnArrayPin->MakeLinkTo(UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_Self));
|
2025-05-23 15:33:18 -04:00
|
|
|
CompilerContext.CopyPinLinksToIntermediate(*FindPinChecked(ErrorPinName), *UnpackNode->FindPinChecked(TEXT("WrongType")));
|
2025-04-08 18:53:26 -04:00
|
|
|
CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(TEXT("Result")));
|
|
|
|
|
ThenPin->MakeLinkTo(UnpackNode->GetExecPin());
|
|
|
|
|
ThenPin = UnpackNode->FindPinChecked(TEXT("Success"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there is an extra results pin, hook it up.
|
|
|
|
|
if (ParsedProto.ExtraReturnValues)
|
|
|
|
|
{
|
|
|
|
|
UEdGraphPin *ExtraPin = FindPinChecked(ExtraResultsPinName);
|
|
|
|
|
UFunction *DiscardFunc = UlxLuaValues::StaticClass()->FindFunctionByName(TEXT("DiscardBeforeCursor"));
|
|
|
|
|
UK2Node_CallFunction *DiscardNode = MakeCallFunctionNode(DiscardFunc);
|
|
|
|
|
ReturnArrayPin->MakeLinkTo(DiscardNode->FindPinChecked(UEdGraphSchema_K2::PN_Self));
|
|
|
|
|
ThenPin->MakeLinkTo(DiscardNode->GetExecPin());
|
|
|
|
|
ThenPin = DiscardNode->GetThenPin();
|
|
|
|
|
CompilerContext.MovePinLinksToIntermediate(*ExtraPin, *ReturnArrayPin);
|
|
|
|
|
}
|
2025-02-26 14:47:53 -05:00
|
|
|
}
|
2025-02-24 16:46:05 -05:00
|
|
|
|
|
|
|
|
// Link up the Exec pins.
|
|
|
|
|
CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin());
|
2025-04-07 18:00:45 -04:00
|
|
|
CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *ThenPin);
|
2025-02-24 16:46:05 -05:00
|
|
|
|
2025-03-28 23:31:44 -04:00
|
|
|
|
2025-02-24 16:46:05 -05:00
|
|
|
BreakAllNodeLinks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
UK2Node::ERedirectType UK2Node_LuaInvoke::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
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<const UEdGraphSchema_K2>(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<UK2Node>(NewPin->GetOwningNode()))
|
|
|
|
|
{
|
|
|
|
|
// if you don't have matching pin, now check if there is any redirect param set
|
|
|
|
|
TArray<FString> 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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
bool UK2Node_LuaInvoke::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
// The function pin cannot be connected.
|
2025-03-28 23:31:44 -04:00
|
|
|
if (MyPin->PinName == FunctionPinName)
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
OutReason = LOCTEXT("Error_FunctionPrototypeMustBeHardwired", "Lua function prototype must be a hardwired constant.").ToString();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
void UK2Node_LuaInvoke::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 16:31:16 -04:00
|
|
|
FText UK2Node_LuaInvoke::GetMenuCategory() const
|
2025-02-24 16:46:05 -05:00
|
|
|
{
|
2025-03-19 16:01:38 -04:00
|
|
|
return FText::FromString(FString(TEXT("Luprex|Lua")));
|
2025-02-24 16:46:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|