//////////////////////////////////////////////////////////// // // LuaCall.h // // Low-level functions for calling Lua from blueprints. // The "Call Lua" K2Node macroexpands into sequences // of these functions. // // Also contains UlxLuaValues, which stores return // values from Lua calls, and FlxParsedProto, which // parses Lua function prototype strings. // //////////////////////////////////////////////////////////// #pragma once #include "CoreMinimal.h" #include "EdGraph/EdGraphPin.h" #include #include #include "Kismet/BlueprintFunctionLibrary.h" #include "LuaCall.generated.h" class UlxLuaValues; //////////////////////////////////////////////////////////// // // FlxParsedProto // // The first argument to LuaCallNode is a function // prototype of the form: // // class.name(int arg1, int arg2) : int ret1 // // Return values can be omitted. The last return // value can be an ellipsis. The class name can be // asterisk. // //////////////////////////////////////////////////////////// class FlxParsedProto { public: struct Pin { FString Type; FString Name; Pin(const FString &T, const FString &N) : Type(T), Name(N) {} }; // If parsing generated an error, the error // message is stored here. // FString ErrorMessage; // The results of parsing the prototype. // FString ClassName; FString FunctionName; TArray Arguments; TArray ReturnValues; bool ExtraReturnValues; // Used internally to help generate the error // message. // TArray Tokens; int NextToken; private: // 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(); // Make a syntax error message, using the tokens. // void Syntax(); // Empty out the FlxParsedProto. // void Empty(); // Parse comma-separated "type name" pairs into ReturnValues. // void ParseReturnValues(); // Parse a full function prototype (assumes already tokenized). // void Parse(); // Tokenize the input string. // bool Tokenize(const FString &str); public: // Parse a full function prototype. // static FlxParsedProto ParsePrototype(const FString &str); // Parse just a comma-separated list of "type name" pairs. // static FlxParsedProto ParseReturnValuesOnly(const FString &str); private: FlxParsedProto() { Empty(); } }; //////////////////////////////////////////////////////////// // // UlxLuaCallLibrary // // Low-level functions that LuaCallNode macroexpands // into. Every LuaCallNode expands into the following // sequence of functions: // // * LuaCallBegin to set class/function name. // * LuaCallArgument_XXX to pack arguments. // * LuaCallInvoke or LuaCallProbe to call. // * LuaCallReturnValue_XXX to fetch results. // // The buffers used by these functions are global // variables in LuprexGameModeBase. It is ok to use // globals, because blueprint is single-threaded. // //////////////////////////////////////////////////////////// UCLASS() class INTEGRATION_API UlxLuaCallLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: // Get an argument packing function or a return // value unpacking function. // static UFunction *GetArgumentPacker(const FString &Type); static UFunction *GetReturnValueUnpacker(const FString &Type); // Get the types supported for arguments and // return values, as a documentation string. // static FString AllFunctionsWithPrefix(const TCHAR *Prefix); static FString AllKnownArgumentTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallArgument_")); } static FString AllKnownReturnValueTypes() { return AllFunctionsWithPrefix(TEXT("LuaCallReturnValue_")); } public: //////////////////////////////////////////////////////// // // Functions that initiate and complete the lua call. // //////////////////////////////////////////////////////// UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Call Lua Function") static void InvokeLuaExpr(UObject *context, const FString &Code); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallBegin(UObject *context, const FString &ClassName, const FString &FunctionName); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallInvoke(UObject *context, AActor *Place); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", ExpandBoolAsExecs="ReturnValue", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static bool LuaCallProbe(UObject *context, AActor *Place, UlxLuaValues *&ReturnArray); //////////////////////////////////////////////////////// // // Functions that pack arguments into the call buffer. // //////////////////////////////////////////////////////// UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_string(UObject *context, const FString &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_name(UObject *context, const FName &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_float(UObject *context, double Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_int(UObject *context, int Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_vector(UObject *context, const FVector &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_vector2d(UObject *context, const FVector2D &Value); UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_boolean(UObject *context, bool Value); }; //////////////////////////////////////////////////////////// // // UlxLuaValues // // Stores an array of values returned by Lua. // Provides a cursor-based API for reading values // one at a time with type checking. // //////////////////////////////////////////////////////////// UCLASS(BlueprintType) class INTEGRATION_API UlxLuaValues : public UObject { GENERATED_BODY() private: // The raw serialized data returned by Lua. // std::string Serialized; // For each chunk, the type of the chunk. // TArray Types; // For each chunk, a pointer to the serialized // data. // TArray Data; // The current cursor. // int Cursor = 0; // Saved cursor for rollback on error. // int SavedCursor = 0; private: // Clear everything. // void Empty(); // Compare two types for equality. // static ElxSuccessOrWrongType CheckType(ElxLuaValueType Type, ElxLuaValueType Desired); public: UlxLuaValues() {} // Copies the data, then preprocesses it to make // sure it's all valid. Returns false if invalid. // bool Initialize(std::string_view data); // Remove all elements before the cursor. // void DiscardBeforeCursor(); // Convert a lua values array into a string. // The string shows all the values, including those // before the cursor, and the cursor as well. // UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") FString DebugString() const; // Return the number of elements remaining after the cursor. // UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") int Length() const { return Types.Num() - Cursor; } // Return the total number of elements, including both those // before and after the cursor. // UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") int TotalLength() const { return Types.Num(); } // Get the current position of the cursor. // UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") int GetCursor() const { return Cursor; } // Set the position of the cursor. The value is clamped to // be within the array. // UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") void SetCursor(int n) { Cursor = FMath::Clamp(n, 0, Types.Num()); } // Get the type of the next element. // UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") ElxLuaValueType NextType() const; // Return true if the type of the next element is what you say. // UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") bool IsNextType(ElxLuaValueType Type) const { return NextType() == Type; } // Switch-statement that depends on the type of the next element. // UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "ReturnValue"), Category = "Luprex|Lua Value Array") ElxLuaValueType SwitchNextType() { return NextType(); } // Allows you to pass a LuaValues to Format Message. // UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Lua Value Array") static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name); //////////////////////////////////////////////////////// // // Routines that are used by the ReadLuaValues K2Node // to read values out. // //////////////////////////////////////////////////////// UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadString(ElxSuccessOrWrongType &Status, FString &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadName(ElxSuccessOrWrongType &Status, FName &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadFloat(ElxSuccessOrWrongType &Status, double &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadInt(ElxSuccessOrWrongType &Status, int &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadVector(ElxSuccessOrWrongType &Status, FVector &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadVector2D(ElxSuccessOrWrongType &Status, FVector2D &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result); //////////////////////////////////////////////////////// // // Other routines used by the ReadLuaValues K2Node // //////////////////////////////////////////////////////// UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Lua Value Array") UlxLuaValues* SaveCursor() { SavedCursor = Cursor; return this; } };