#pragma once #include "CoreMinimal.h" #include "EdGraph/EdGraphPin.h" #include "LuaCall.generated.h" class UlxLuaValues; ///////////////////////////////////////////////////////////////// // // These are the types that can actually be packed into // a serialized buffer. // ///////////////////////////////////////////////////////////////// UENUM(BlueprintType) enum class ElxLuaValueType : uint8 { End, String, Name, Float, Boolean, Vector }; ///////////////////////////////////////////////////////////////// // // A general-purpose 'success or error' type. // ///////////////////////////////////////////////////////////////// UENUM(BlueprintType) enum class ElxSuccessOrError : uint8 { Success, Error, }; ///////////////////////////////////////////////////////////////// // // This is a little parser that parses Lua function 'prototypes'. // The prototypes look like this: // // class.name(int arg1, int arg2) : int ret1, int ret2 // // The return values can be omitted if the function has no return // values. Optionally, there can be one last return value which // is just an ellipsis. The class name can be asterisk. // // For more information about the meaning of all this, see the docs // for the 'Call Lua' blueprint node. // ///////////////////////////////////////////////////////////////// class FlxParsedProto { public: struct Pin { FString Type; FString Name; Pin(const FString &T, const FString &N) : Type(T), Name(N) {} }; FString ErrorMessage; TArray Tokens; int NextToken; FString ClassName; FString FunctionName; TArray Arguments; TArray ReturnValues; bool ExtraReturnValues; 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); } }; ///////////////////////////////////////////////////////////////// // // To make a Lua Call from inside of a blueprint, use the convenient "Call Lua" // blueprint node. This node macroexpands into a sequence of low-level // function calls. This library contains the low-level functions that // "Call Lua" macroexpands into. // // The procedure for making a lua call using the low-level functions is as follows: // // * Use LuaCallBegin to put the class name and function name into the argument buffer. // * Use LuaCallArgumentXXX to put arguments into the argument buffer. // * Use LuaCallInvoke or LuaCallProbe to actually make the call. // * Use LuaCallReturnValueXXX to fetch return values from the return buffer. // // The two buffers are basically global variables, they are part of the // LuprexGameModeBase. This is okay because blueprint is single-threaded. // // The following three libraries contain all the low-level functions. // ///////////////////////////////////////////////////////////////// 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 do miscellaneous things. // 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); }; ///////////////////////////////////////////////////////////////// // // This class stores an array of values that were returned by Lua. // ///////////////////////////////////////////////////////////////// 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, log an error and return false. // static ElxSuccessOrError CheckType(ElxLuaValueType Type, ElxLuaValueType Desired); public: UlxLuaValues() { Cursor = 0; } // Copies the data, and then preprocesses it to make sure // it's all valid. If it's not valid, returns false. // 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(ElxSuccessOrError &Status, FString &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadName(ElxSuccessOrError &Status, FName &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadFloat(ElxSuccessOrError &Status, double &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadInt(ElxSuccessOrError &Status, int &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadVector(ElxSuccessOrError &Status, FVector &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadVector2D(ElxSuccessOrError &Status, FVector2D &Result); UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array") void ReadBoolean(ElxSuccessOrError &Status, bool &Result); };