From 3423c018072a8ccbaf4c0e218f41383d69cc7e55 Mon Sep 17 00:00:00 2001 From: jyelon Date: Tue, 8 Apr 2025 18:53:26 -0400 Subject: [PATCH] Overhaul the handling of return values in LuaProbe --- Content/Luprex/lxGameMode.uasset | 4 +- Source/Integration/LuaCall.cpp | 147 +++++----------------- Source/Integration/LuaCall.h | 33 +---- Source/Integration/LuaCallNode.cpp | 75 ++++++----- Source/Integration/LuprexGameModeBase.cpp | 23 ++-- Source/Integration/LuprexGameModeBase.h | 12 +- 6 files changed, 100 insertions(+), 194 deletions(-) diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index 06631dec..ed5cd998 100644 --- a/Content/Luprex/lxGameMode.uasset +++ b/Content/Luprex/lxGameMode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaea9054f184e0bcf4ba7c5af20d07bad414ed934d62d48e1cf0fa7f5e3aa602 -size 157429 +oid sha256:81eb6a9dc606e6b8475d823ca4c125bf509cb2ec08b0f71eff754d6fe1418f5a +size 156416 diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 8ad6b37e..5ca26575 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -169,16 +169,14 @@ void FlxParsedProto::Parse(const FString &str) { UFunction *UlxLuaCallLibrary::GetArgumentPacker(const FString &Type) { - FString LType = Type.ToLower(); - FName PackerName(FString::Printf(TEXT("LuaCallArgument_%s"), *LType)); + FName PackerName(FString::Printf(TEXT("LuaCallArgument_%s"), *Type)); return UlxLuaCallLibrary::StaticClass()->FindFunctionByName(PackerName); } UFunction *UlxLuaCallLibrary::GetReturnValueUnpacker(const FString &Type) { - FString LType = Type.ToLower(); - FName PackerName(FString::Printf(TEXT("LuaCallReturnValue_%s"), *LType)); - return UlxLuaCallLibrary::StaticClass()->FindFunctionByName(PackerName); + FName PackerName(FString::Printf(TEXT("Read%s"), *Type)); + return UlxLuaValues::StaticClass()->FindFunctionByName(PackerName); } FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix) @@ -223,41 +221,36 @@ void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) } -bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) +bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValues *&ReturnArray) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); if (NotInitialized(sb)) return false; - mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); - FlxStreamBuffer &result = mode->LuaCallGetResult(); - SimpleDynamicTag tag = result.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::STRING) - { + ReturnArray = mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); + if ((ReturnArray == nullptr) || (ReturnArray->Length() < 1)) + { UE_LOG(LogLuprexIntegration, Error, TEXT("corruption in lua_probe")); + ReturnArray = nullptr; return false; } - FString ErrorMessage = result.read_fstring(); + ElxSuccessOrError Status; + FString ErrorMessage; + ReturnArray->ReadString(Status, ErrorMessage); + if (Status != ElxSuccessOrError::Success) + { + UE_LOG(LogLuprexIntegration, Error, TEXT("corruption in lua_probe")); + ReturnArray = nullptr; + return false; + } if (!ErrorMessage.IsEmpty()) { UE_LOG(LogLuprex, Error, TEXT("%s"), *ErrorMessage); + ReturnArray = nullptr; return false; } return true; } -UlxLuaValues *UlxLuaCallLibrary::LuaCallGetRest(UObject *context) -{ - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - UlxLuaValues *Values = NewObject(context); - if (!Values->Initialize(sb.view())) - { - UE_LOG(LogBlueprint, Error, TEXT("Lua call returned corrupt data")); - return nullptr; - } - return Values; -} - ///////////////////////////////////////////////////////////////// // // Argument Packing functions @@ -330,98 +323,6 @@ void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { } -///////////////////////////////////////////////////////////////// -// -// Return Value Unpacking functions -// -///////////////////////////////////////////////////////////////// - - -FString UlxLuaCallLibrary::LuaCallReturnValue_string(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::STRING) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a string")); - return TEXT(""); - } - return sb.read_fstring(); -} - -FName UlxLuaCallLibrary::LuaCallReturnValue_name(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::TOKEN) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a name")); - return FName(); - } - return sb.read_fname(); -} - -double UlxLuaCallLibrary::LuaCallReturnValue_float(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::NUMBER) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a float")); - return 0.0; - } - return sb.read_double(); -} - -int UlxLuaCallLibrary::LuaCallReturnValue_int(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::NUMBER) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a number")); - return 0; - } - return int(sb.read_double()); -} - -FVector UlxLuaCallLibrary::LuaCallReturnValue_vector(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::VECTOR) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a vector")); - return FVector(); - } - return sb.read_fvector(); -} - -FVector2D UlxLuaCallLibrary::LuaCallReturnValue_vector2d(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::VECTOR) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a vector")); - return FVector2D(); - } - FVector v = sb.read_fvector(); - return FVector2D(v.X, v.Y); -} - -bool UlxLuaCallLibrary::LuaCallReturnValue_boolean(UObject *context) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->LuaCallGetResult(); - SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); - if (tag != SimpleDynamicTag::BOOLEAN) - { - UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a boolean")); - return false; - } - return sb.read_bool(); -} - ///////////////////////////////////////////////////////////////// // // Returning the rest of the lua return values as an array. @@ -478,6 +379,8 @@ FString UlxLuaValues::DebugString() const { if (i > 0) Output << TEXT(", "); + if (i == Cursor) Output << TEXT("^ "); + ElxLuaValueType Type = Types[i]; FlxStreamBuffer Decoder(Data[i]); switch (Type) @@ -517,6 +420,16 @@ ElxSuccessOrError UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType return ElxSuccessOrError::Success; } +void UlxLuaValues::DiscardBeforeCursor() +{ + if (Cursor <= 0) return; + int Discard = Cursor; + if (Discard > Types.Num()) Discard = Types.Num(); + Types.RemoveAt(0, Discard); + Data.RemoveAt(0, Discard); + Cursor -= Discard; +} + ElxLuaValueType UlxLuaValues::NextType() const { if (Cursor < 0) return ElxLuaValueType::End; diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index 8705a12e..feb5ab35 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -149,11 +149,8 @@ public: 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); + static bool LuaCallProbe(UObject *context, AActor *Place, UlxLuaValues *&ReturnArray); - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static UlxLuaValues *LuaCallGetRest(UObject *context); - // // Functions that pack arguments into the call buffer. // @@ -178,31 +175,6 @@ public: UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") static void LuaCallArgument_boolean(UObject *context, bool Value); - - // - // Functions that extract return values from the return buffer. - // - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static FString LuaCallReturnValue_string(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static FName LuaCallReturnValue_name(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static double LuaCallReturnValue_float(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static int LuaCallReturnValue_int(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static FVector LuaCallReturnValue_vector(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static FVector2D LuaCallReturnValue_vector2d(UObject *context); - - UFUNCTION(BlueprintCallable, meta = (WorldContext = "context", BlueprintInternalUseOnly = "true"), Category = "Luprex|Call Lua Function") - static bool LuaCallReturnValue_boolean(UObject *context); }; @@ -253,6 +225,9 @@ public: // 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; diff --git a/Source/Integration/LuaCallNode.cpp b/Source/Integration/LuaCallNode.cpp index 7a8708a8..191a33b1 100644 --- a/Source/Integration/LuaCallNode.cpp +++ b/Source/Integration/LuaCallNode.cpp @@ -48,7 +48,7 @@ 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")); -const FName UK2Node_LuaInvoke::ErrorPinName(TEXT("Lua Error")); +const FName UK2Node_LuaInvoke::ErrorPinName(TEXT("Error")); bool UK2Node_LuaInvoke::IsPrefix(const UEdGraphPin *Pin, char Prefix) { @@ -174,8 +174,9 @@ void UK2Node_LuaInvoke::CreateCorrectPins() { Pins.Emplace(*OldPin); OldPins.Remove(Name); + return true; } - return OldPin != nullptr; + return false; }; if (!KeepPin(UEdGraphSchema_K2::PN_Execute)) @@ -239,7 +240,7 @@ void UK2Node_LuaInvoke::CreateCorrectPins() ErrorMsg = FString::Printf(TEXT("Unknown return value type: %s"), *Pin.Type); continue; } - FEdGraphPinType PinType = GetPinType(Accessor->GetReturnProperty()); + FEdGraphPinType PinType = GetPinType(Accessor->FindPropertyByName(TEXT("Result"))); if (!KeepPin(PrefixedName, PinType)) { CreatePin(EGPD_Output, PinType, PrefixedName); @@ -384,58 +385,70 @@ void UK2Node_LuaInvoke::ExpandNode(class FKismetCompilerContext& CompilerContext } // Add the invoke or probe node. + UEdGraphPin *ReturnArrayPin = nullptr; if (IsInvoke()) { UK2Node_CallFunction* ActionNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallInvoke)); - CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PlacePinName, EGPD_Input), *ActionNode->FindPinChecked(TEXT("Place"))); + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PlacePinName), *ActionNode->FindPinChecked(TEXT("Place"))); ThenPin->MakeLinkTo(ActionNode->GetExecPin()); ThenPin = ActionNode->GetThenPin(); } else { UK2Node_CallFunction* ActionNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallProbe)); - CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PlacePinName, EGPD_Input), *ActionNode->FindPinChecked(TEXT("Place"))); - CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(ErrorPinName, EGPD_Output), *ActionNode->FindPinChecked(TEXT("False"))); + CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(PlacePinName), *ActionNode->FindPinChecked(TEXT("Place"))); + CompilerContext.CopyPinLinksToIntermediate(*FindPinChecked(ErrorPinName), *ActionNode->FindPinChecked(TEXT("False"))); + ReturnArrayPin = ActionNode->FindPinChecked(TEXT("ReturnArray")); ThenPin->MakeLinkTo(ActionNode->GetExecPin()); ThenPin = ActionNode->FindPinChecked(TEXT("True")); } - // Add Unpacking operations for all return value pins. - for (const FlxParsedProto::Pin &PinInfo : ParsedProto.ReturnValues) + if (IsInvoke()) { - UEdGraphPin *Pin = FindPinChecked(AddPrefix(PinInfo.Name, 'R')); - UFunction *UnpackingFunc = UlxLuaCallLibrary::GetReturnValueUnpacker(PinInfo.Type); - if (UnpackingFunc == nullptr) + // Make sure we didn't have return values for an invoke. + if (IsInvoke() && ((ParsedProto.ReturnValues.Num() > 0) || (ParsedProto.ExtraReturnValues))) { - // This codepath should be unreachable, but just in case. - CompilerContext.MessageLog.Error(TEXT("All return value pins must have known types.")); - continue; + CompilerContext.MessageLog.Error(TEXT("Lua Call in Invoke mode does not support return values")); } - UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(UnpackingFunc); - CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue)); - ThenPin->MakeLinkTo(UnpackNode->GetExecPin()); - ThenPin = UnpackNode->GetThenPin(); } - - // If there is an extra results pin, hook it up. - if (ParsedProto.ExtraReturnValues) + else { - UEdGraphPin *Pin = FindPinChecked(ExtraResultsPinName); - UK2Node_CallFunction *UnpackNode = MakeCallFunctionNode(LuaCallLibraryFunction(LuaCallGetRest)); - CompilerContext.MovePinLinksToIntermediate(*Pin, *UnpackNode->FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue)); - ThenPin->MakeLinkTo(UnpackNode->GetExecPin()); - ThenPin = UnpackNode->GetThenPin(); + // 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)); + CompilerContext.CopyPinLinksToIntermediate(*FindPinChecked(ErrorPinName), *UnpackNode->FindPinChecked(TEXT("Error"))); + 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); + } } // Link up the Exec pins. CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *BeginNode->GetExecPin()); CompilerContext.MovePinLinksToIntermediate(*GetThenPin(), *ThenPin); - // Make sure we didn't have return values for an invoke. - if (IsInvoke() && ((ParsedProto.ReturnValues.Num() > 0) || (ParsedProto.ExtraReturnValues))) - { - CompilerContext.MessageLog.Error(TEXT("Lua Call in Invoke mode does not support return values")); - } BreakAllNodeLinks(); } diff --git a/Source/Integration/LuprexGameModeBase.cpp b/Source/Integration/LuprexGameModeBase.cpp index c17de555..5f0d6bdf 100644 --- a/Source/Integration/LuprexGameModeBase.cpp +++ b/Source/Integration/LuprexGameModeBase.cpp @@ -6,6 +6,7 @@ #include "ConsoleOutput.h" #include "Tangible.h" #include "TangibleManager.h" +#include "LuaCall.h" #include "Blueprint/UserWidget.h" #include "Kismet/GameplayStatics.h" @@ -168,29 +169,35 @@ void ALuprexGameModeBase::UpdatePossessedTangible() { } } -void ALuprexGameModeBase::LuaCallEnd(InvocationKind kind, int64 place_id) { +UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(InvocationKind kind, int64 place_id) { std::string_view datapk = LuaCallBuffer.view(); FlxLockedWrapper w(LockableWrapper); if (place_id == 0) place_id = w.GetActor(); uint32_t retpklen; const char *retpk; w->play_call_function(w.Get(), kind, place_id, datapk.size(), datapk.data(), &retpklen, &retpk); - LuaCallResult.open(std::string_view(retpk, retpklen)); + if (kind == InvocationKind::LUA_PROBE) + { + UlxLuaValues *Result = NewObject(this); + Result->Initialize(std::string_view(retpk, retpklen)); + return Result; + } + else return nullptr; } -void ALuprexGameModeBase::LuaCallEnd(InvocationKind kind) { - LuaCallEnd(kind, int64(0)); +UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(InvocationKind kind) { + return LuaCallEnd(kind, int64(0)); } -void ALuprexGameModeBase::LuaCallEnd(InvocationKind kind, AActor *place) { +UlxLuaValues *ALuprexGameModeBase::LuaCallEnd(InvocationKind kind, AActor *place) { if (place == nullptr) { - LuaCallEnd(kind, int64(0)); + return LuaCallEnd(kind, int64(0)); } else { UlxTangible *tan = UlxTangible::GetActorTangibleOrLog(place); if (tan == nullptr) { - LuaCallResult.clear(); + return NewObject(this); } else { - LuaCallEnd(kind, tan->TangibleId); + return LuaCallEnd(kind, tan->TangibleId); } } } diff --git a/Source/Integration/LuprexGameModeBase.h b/Source/Integration/LuprexGameModeBase.h index e637ba13..5459f806 100644 --- a/Source/Integration/LuprexGameModeBase.h +++ b/Source/Integration/LuprexGameModeBase.h @@ -124,11 +124,10 @@ public: // FlxStreamBuffer &LuaCallBegin() { LuaCallBuffer.clear(); return LuaCallBuffer; } FlxStreamBuffer &LuaCallGetBuffer() { return LuaCallBuffer; } - void LuaCallEnd(InvocationKind kind); - void LuaCallEnd(InvocationKind kind, int64 place_id); - void LuaCallEnd(InvocationKind kind, AActor *place); - FlxStreamBuffer &LuaCallGetResult() { return LuaCallResult; } - void LuaCallClear() { LuaCallBuffer.clear(); LuaCallResult.clear(); } + UlxLuaValues *LuaCallEnd(InvocationKind kind); + UlxLuaValues *LuaCallEnd(InvocationKind kind, int64 place_id); + UlxLuaValues *LuaCallEnd(InvocationKind kind, AActor *place); + void LuaCallClear() { LuaCallBuffer.clear(); } // Execute a debugging command, typed on the GUI. void ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs); @@ -191,9 +190,8 @@ public: // To access it, construct a FlxLockedWrapper. FlxLockableWrapper LockableWrapper; - // The Lua Call assembly buffer. + // The Lua Call Assembly Buffer. FlxStreamBuffer LuaCallBuffer; - FlxStreamBuffer LuaCallResult; // This utility runs the luprex update and socket update in a thread. FTriggeredTask LuprexUpdateTask;