//////////////////////////////////////////////////////////// // // 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 "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 a function prototype. // void Parse(const FString &proto); public: // Construct with a prototype. // FlxParsedProto(const FString &str) { Parse(str); } }; //////////////////////////////////////////////////////////// // // 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 UObject { 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; private: // Clear everything. // void Empty(); // Compare two types. If they aren't equal, // possibly log an error, and return a status // code. // static ElxSuccessOrWrongType CheckType(bool LogErrorOnWrongType, ElxLuaValueType Type, ElxLuaValueType Desired); public: UlxLuaValues() { Cursor = 0; } // Copies the data, then preprocesses it to make // sure it's all valid. Returns false if invalid. // bool Initialize(std::string_view data); UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") void DiscardBeforeCursor(); UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") FString DebugString() const; UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") int Length() const { return Types.Num(); } UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") int GetCursor() const { return Cursor; } UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") void SetCursor(int n) { Cursor = n; } UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") ElxLuaValueType NextType() const; UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") bool IsNextType(ElxLuaValueType Type) const { return NextType() == Type; } UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "ReturnValue"), Category = "Luprex|Lua Value Array") ElxLuaValueType SwitchNextType() { return NextType(); } UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadString(ElxSuccessOrWrongType &Status, FString &Result, bool LogErrorOnWrongType = false); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadName(ElxSuccessOrWrongType &Status, FName &Result, bool LogErrorOnWrongType = false); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadFloat(ElxSuccessOrWrongType &Status, double &Result, bool LogErrorOnWrongType = false); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadInt(ElxSuccessOrWrongType &Status, int &Result, bool LogErrorOnWrongType = false); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadVector(ElxSuccessOrWrongType &Status, FVector &Result, bool LogErrorOnWrongType = false); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadVector2D(ElxSuccessOrWrongType &Status, FVector2D &Result, bool LogErrorOnWrongType = false); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool LogErrorOnWrongType = false); };