From d35125eb7043e78b9a46e516a58e666bb4769651 Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 7 Apr 2025 16:48:27 -0400 Subject: [PATCH] Lots of work on look-at widgets --- Content/Luprex/Widgets/lxConsoleWidget.uasset | 3 - .../Luprex/Widgets/lxCrosshairImage.uasset | 3 - .../Luprex/Widgets/lxCrosshairWidget.uasset | 3 - Content/Luprex/lxCrosshairImage.uasset | 4 +- Content/Luprex/lxGameMode.uasset | 4 +- Content/Luprex/lxUtilityMacroLibrary.uasset | 4 +- Content/Widgets/WB_Console.uasset | 3 + Content/Widgets/WB_Crosshair.uasset | 3 + Content/Widgets/WB_Hotkeys.uasset | 3 + Content/Widgets/lxCrosshairImage.uasset | 3 + Source/Integration/AssetLookup.cpp | 95 +++++-- Source/Integration/AssetLookup.h | 37 ++- Source/Integration/BlueprintErrors.cpp | 2 +- Source/Integration/Integration.Build.cs | 1 + Source/Integration/LuaCall.cpp | 242 ++++++++++++++++-- Source/Integration/LuaCall.h | 142 ++++++++++ Source/Integration/LuprexGameModeBase.cpp | 34 ++- Source/Integration/LuprexGameModeBase.h | 32 ++- Source/Integration/StringDecoder.cpp | 151 ----------- Source/Integration/StringDecoder.h | 143 ----------- Source/Integration/UtilityLibrary.h | 3 + luprex/cpp/core/world-core.cpp | 2 +- luprex/cpp/core/world.hpp | 2 +- luprex/lua/login.lua | 14 +- 24 files changed, 554 insertions(+), 379 deletions(-) delete mode 100644 Content/Luprex/Widgets/lxConsoleWidget.uasset delete mode 100644 Content/Luprex/Widgets/lxCrosshairImage.uasset delete mode 100644 Content/Luprex/Widgets/lxCrosshairWidget.uasset create mode 100644 Content/Widgets/WB_Console.uasset create mode 100644 Content/Widgets/WB_Crosshair.uasset create mode 100644 Content/Widgets/WB_Hotkeys.uasset create mode 100644 Content/Widgets/lxCrosshairImage.uasset diff --git a/Content/Luprex/Widgets/lxConsoleWidget.uasset b/Content/Luprex/Widgets/lxConsoleWidget.uasset deleted file mode 100644 index 7329c3ad..00000000 --- a/Content/Luprex/Widgets/lxConsoleWidget.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:46d9c6bd495d3b1b2e39bf9e8c22206e5caf878ad107594b6030f93efc87bcb3 -size 70316 diff --git a/Content/Luprex/Widgets/lxCrosshairImage.uasset b/Content/Luprex/Widgets/lxCrosshairImage.uasset deleted file mode 100644 index 5e0afff0..00000000 --- a/Content/Luprex/Widgets/lxCrosshairImage.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a1a5ba9cdf9bb9172629790c999fc68eabaf46e49e680aab347031b5afbde410 -size 8098 diff --git a/Content/Luprex/Widgets/lxCrosshairWidget.uasset b/Content/Luprex/Widgets/lxCrosshairWidget.uasset deleted file mode 100644 index cc0ad4ce..00000000 --- a/Content/Luprex/Widgets/lxCrosshairWidget.uasset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e0971df568f92793c9b3efa48ff9a74d8ece561811f692b167a16162e81700f9 -size 56644 diff --git a/Content/Luprex/lxCrosshairImage.uasset b/Content/Luprex/lxCrosshairImage.uasset index ed91d253..4113b0b3 100644 --- a/Content/Luprex/lxCrosshairImage.uasset +++ b/Content/Luprex/lxCrosshairImage.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3560d4788e77b90c908e26cd058469f939122c2b8cfe2c75ef8fe0a40d404503 -size 1156 +oid sha256:670413c9e1d9992d746405287d04fd27a7f69a6daa30ee3f03091e7499d13969 +size 1425 diff --git a/Content/Luprex/lxGameMode.uasset b/Content/Luprex/lxGameMode.uasset index cff5a124..1b382459 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:fc18fb741f71504c6cc8da211e4a7dffe9a07b03d816cf1cd04c68a6b84893f4 -size 117713 +oid sha256:615e0ffa732710680a3b3ed3cb35a391f49f965c5d8a49f82e03f6dfb7577df8 +size 161973 diff --git a/Content/Luprex/lxUtilityMacroLibrary.uasset b/Content/Luprex/lxUtilityMacroLibrary.uasset index c110ed0f..058bad81 100644 --- a/Content/Luprex/lxUtilityMacroLibrary.uasset +++ b/Content/Luprex/lxUtilityMacroLibrary.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:967f7f152d309c6983053d7a5def2eb72eb2ecc7401249fa9ec5f5b7d4e3eb50 -size 24274 +oid sha256:491b5ab58f578603052c33d5d23c74c0c6536ebbfb6fa8b057e9d504b2a92b42 +size 37502 diff --git a/Content/Widgets/WB_Console.uasset b/Content/Widgets/WB_Console.uasset new file mode 100644 index 00000000..ad32a2b2 --- /dev/null +++ b/Content/Widgets/WB_Console.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bab42c29be34eed14d47d31594858d3f12376603ae80ffaf7948ef38b9c6686 +size 70196 diff --git a/Content/Widgets/WB_Crosshair.uasset b/Content/Widgets/WB_Crosshair.uasset new file mode 100644 index 00000000..863d4c82 --- /dev/null +++ b/Content/Widgets/WB_Crosshair.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d34efb7d4caebfa5f4437012dbf0e5b6a20e8b121b51d189ea411af7d47f58df +size 54264 diff --git a/Content/Widgets/WB_Hotkeys.uasset b/Content/Widgets/WB_Hotkeys.uasset new file mode 100644 index 00000000..6b86f9ca --- /dev/null +++ b/Content/Widgets/WB_Hotkeys.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:174590f11c780fd06aa29d3475a886b13c4ac843dbdabaecc480f912ae64ba96 +size 41205 diff --git a/Content/Widgets/lxCrosshairImage.uasset b/Content/Widgets/lxCrosshairImage.uasset new file mode 100644 index 00000000..dbbce351 --- /dev/null +++ b/Content/Widgets/lxCrosshairImage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dca13b2122893f9b1a14667a5c1a60a362368c88b3715c5288afdd5c038b0acf +size 8084 diff --git a/Source/Integration/AssetLookup.cpp b/Source/Integration/AssetLookup.cpp index 616bfffb..60ad8314 100644 --- a/Source/Integration/AssetLookup.cpp +++ b/Source/Integration/AssetLookup.cpp @@ -4,6 +4,9 @@ #include "AssetRegistry/AssetData.h" #include "AssetRegistry/AssetRegistryState.h" #include "LuprexGameModeBase.h" +#include "Components/Widget.h" +#include "WidgetBlueprint.h" +#include "Blueprint/UserWidget.h" void UlxAssetLookup::RebuildIndex() { @@ -12,6 +15,7 @@ void UlxAssetLookup::RebuildIndex() IAssetRegistry::GetChecked().WaitForCompletion(); ScanTangibles(); ScanStaticMeshes(); + ScanWidgets(); } void UlxAssetLookup::ScanTangibles() @@ -27,7 +31,7 @@ void UlxAssetLookup::ScanTangibles() UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d assets in /Game/Tangibles"), FoundData.Num()); for (const FAssetData &Data : FoundData) { - FString Path = Data.GetObjectPathString(); + FString Path = Data.GetObjectPathString() + TEXT("_C"); CachedTangibles.Add(Data.AssetName, Path); } } @@ -42,7 +46,7 @@ void UlxAssetLookup::ScanStaticMeshes() AssetFilter.bRecursivePaths = true; IAssetRegistry::GetChecked().GetAssets(AssetFilter, FoundData); - UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d static mesh assets"), FoundData.Num()); + UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d assets in /Game/StaticMeshes"), FoundData.Num()); for (const FAssetData &Data : FoundData) { FString Path = Data.GetObjectPathString(); @@ -50,10 +54,26 @@ void UlxAssetLookup::ScanStaticMeshes() } } +void UlxAssetLookup::ScanWidgets() +{ + TArray FoundData; + FARFilter AssetFilter; + AssetFilter.PackagePaths.Add(FName(TEXT("/Game/Widgets"))); + AssetFilter.ClassPaths.Add(UWidgetBlueprint::StaticClass()->GetClassPathName()); + AssetFilter.bIncludeOnlyOnDiskAssets = true; + AssetFilter.bRecursivePaths = true; + IAssetRegistry::GetChecked().GetAssets(AssetFilter, FoundData); + + UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d assets in /Game/Widgets"), FoundData.Num()); + for (const FAssetData &Data : FoundData) + { + FString Path = Data.GetObjectPathString() + TEXT("_C"); + CachedWidgets.Add(Data.AssetName, Path); + } +} + FString UlxAssetLookup::TangibleLoadPath(const FName &AssetName) const { - FScopeLock lock(&Mutex); - const FString *Result = CachedTangibles.Find(AssetName); if (Result == nullptr) return TEXT(""); return *Result; @@ -61,13 +81,18 @@ FString UlxAssetLookup::TangibleLoadPath(const FName &AssetName) const FString UlxAssetLookup::StaticMeshLoadPath(const FName &AssetName) const { - FScopeLock lock(&Mutex); - const FString *Result = CachedStaticMeshes.Find(AssetName); if (Result == nullptr) return TEXT(""); return *Result; } +FString UlxAssetLookup::WidgetLoadPath(const FName &AssetName) const +{ + const FString *Result = CachedWidgets.Find(AssetName); + if (Result == nullptr) return TEXT(""); + return *Result; +} + UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const FString &Name) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); @@ -77,18 +102,16 @@ UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const F UE_LOG(LogLuprexIntegration, Error, TEXT("Static mesh not on search path: %s"), *Name); return nullptr; } - - FString FullPath = Path; - UStaticMesh *Result = LoadObject(nullptr, *FullPath); + UStaticMesh *Result = LoadObject(nullptr, *Path); if (Result == nullptr) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load static mesh: %s"), *FullPath); + UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load static mesh: %s"), *Path); return nullptr; } return Result; } -UClass *UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name) { +TSubclassOf UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name)); if (Path.IsEmpty()) @@ -96,21 +119,61 @@ UClass *UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FSt UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible not on search path: %s"), *Name); return nullptr; } - FString FullPath = Path + TEXT("_C"); - UClass *Result = LoadObject(nullptr, *FullPath); + UClass *Result = LoadObject(nullptr, *Path); if (Result == nullptr) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load tangible class: %s"), *FullPath); + UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load tangible class: %s"), *Path); return nullptr; } if (!Result->IsChildOf(AActor::StaticClass())) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible class is not an actor: %s"), *FullPath); + UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible class is not an actor: %s"), *Path); return nullptr; } UFunction *aqchanged = Result->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); if ((aqchanged == nullptr)||(aqchanged->ParmsSize != 0)) { - UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible does not have 'Animation Queue Changed' function: %s"), *FullPath); + UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible does not have 'Animation Queue Changed' function: %s"), *Path); return nullptr; } return Result; } + +TSubclassOf UlxAssetLookup::GetWidgetByName(const UObject *Context, const FString &Name) { + ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); + FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); + if (Path.IsEmpty()) + { + UE_LOG(LogLuprexIntegration, Error, TEXT("Widget not on search path: %s"), *Name); + return nullptr; + } + UClass *Result = LoadObject(nullptr, *Path); + if (Result == nullptr) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load widget blueprint: %s"), *Path); + return nullptr; + } + if (!Result->IsChildOf(UUserWidget::StaticClass())) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Blueprint is not a Widget Blueprint: %s"), *Path); + return nullptr; + } + return Result; +} + +TSubclassOf UlxAssetLookup::GetLookAtWidgetByName(const UObject *Context, const FString &Name) { + ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); + FString Path = mode->GetAssetLookup()->WidgetLoadPath(FName(FString("WB_") + Name)); + if (Path.IsEmpty()) + { + UE_LOG(LogLuprexIntegration, Error, TEXT("Widget not on search path: %s"), *Name); + return nullptr; + } + UClass *Result = LoadObject(nullptr, *Path); + if (Result == nullptr) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load widget blueprint: %s"), *Path); + return nullptr; + } + if (!Result->IsChildOf(UlxLookAtWidget::StaticClass())) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Blueprint is not a Luprex Look-At Widget: %s"), *Path); + return nullptr; + } + return Result; +} + diff --git a/Source/Integration/AssetLookup.h b/Source/Integration/AssetLookup.h index 54db6aee..ce9d4aaa 100644 --- a/Source/Integration/AssetLookup.h +++ b/Source/Integration/AssetLookup.h @@ -6,14 +6,17 @@ #include "CommonTypes.h" #include "AssetLookup.generated.h" +class AActor; +class UUserWidget; +class UlxLookAtWidget; +class UStaticMesh; + UCLASS(MinimalAPI) class UlxAssetLookup : public UObject { GENERATED_BODY() private: - mutable FCriticalSection Mutex; - // Map from asset name to full loadable path. UPROPERTY() TMap CachedTangibles; @@ -22,22 +25,40 @@ private: UPROPERTY() TMap CachedStaticMeshes; -public: - void RebuildIndex(); + // Map from asset name to full loadable path. + UPROPERTY() + TMap CachedWidgets; + +private: void ScanTangibles(); void ScanStaticMeshes(); + void ScanWidgets(); - // Get the full path name of a +public: + void RebuildIndex(); + + // Get the full LoadObject path of a Tangible. FString TangibleLoadPath(const FName &AssetName) const; - // Get a static mesh by its asset name. + // Get the full LoadObject path of a Static Mesh. FString StaticMeshLoadPath(const FName &AssetName) const; + // Get the full LoadObject path of a Widget Blueprint. + FString WidgetLoadPath(const FName &AssetName) const; + // Get a static mesh by name UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") static UStaticMesh *GetStaticMeshByName(const UObject *Context, const FString &Name); - // Get a static mesh by name + // Get a tangible class by name UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") - static UClass *GetTangibleClassByName(const UObject *Context, const FString &Name); + static TSubclassOf GetTangibleClassByName(const UObject *Context, const FString &Name); + + // Get a widget blueprint by name + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") + static TSubclassOf GetWidgetByName(const UObject *Context, const FString &Name); + + // Get a look-at widget blueprint by name + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") + static TSubclassOf GetLookAtWidgetByName(const UObject *Context, const FString &Name); }; diff --git a/Source/Integration/BlueprintErrors.cpp b/Source/Integration/BlueprintErrors.cpp index 2d134900..95029434 100644 --- a/Source/Integration/BlueprintErrors.cpp +++ b/Source/Integration/BlueprintErrors.cpp @@ -1,6 +1,6 @@ #include "BlueprintErrors.h" -#include "StringDecoder.h" +#include "LuaCall.h" #include "Internationalization/TextFormatter.h" #include "Kismet/KismetSystemLibrary.h" #include "Kismet2/KismetDebugUtilities.h" diff --git a/Source/Integration/Integration.Build.cs b/Source/Integration/Integration.Build.cs index d82e62c4..c410d651 100644 --- a/Source/Integration/Integration.Build.cs +++ b/Source/Integration/Integration.Build.cs @@ -25,6 +25,7 @@ public class Integration : ModuleRules "KismetWidgets", "BlueprintGraph", "UMG", + "UMGEditor", }); // Uncomment if you are using Slate UI diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 4528ba09..ff12352a 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -5,15 +5,14 @@ #include "EdGraphSchema_K2.h" -static void FatalBlueprintError(const TCHAR *message) { - FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::FatalError, FText::FromString(FString(message))); - FBlueprintCoreDelegates::ThrowScriptException(FFrame::GetThreadLocalTopStackFrame()->Object, *FFrame::GetThreadLocalTopStackFrame(), ExceptionInfo); -} -static void CheckNotEmpty(const FlxStreamBuffer &sb) { - if (sb.empty()) { - FatalBlueprintError(TEXT("Must use LuaCallBegin before other LuaCall steps")); +static bool NotInitialized(const FlxStreamBuffer &sb) { + if (sb.empty()) + { + UE_LOG(LogBlueprint, Error, TEXT("Must use LuaCallBegin before other LuaCall steps")); + return true; } + return false; } static constexpr uint64_t ParseNameAsToken(std::string_view str) { @@ -219,8 +218,8 @@ void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); - mode->LuaCallEnd(InvocationKind::LUA_INVOKE, place); + if (NotInitialized(sb)) return; + mode->LuaCallEnd(InvocationKind::LUA_INVOKE, place); } @@ -228,7 +227,7 @@ void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); } @@ -254,7 +253,7 @@ UlxLuaValues *UlxLuaCallLibrary::LuaCallGetRest(UObject *context) void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); sb.write_string(pstring); } @@ -263,12 +262,13 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam FTCHARToUTF8 utf8str(pname.ToString()); std::string_view namestr(utf8str.Get(), utf8str.Length()); uint64_t tokvalue = ParseNameAsToken(namestr); - if ((tokvalue == 0) && !namestr.empty()) { - FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); + if ((tokvalue == 0) && !namestr.empty()) + { + UE_LOG(LogBlueprint, Error, TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); } ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); sb.write_string(namestr); } @@ -276,7 +276,7 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_double(pfloat); } @@ -284,7 +284,7 @@ void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_double(value); } @@ -292,7 +292,7 @@ void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &pvector) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_fvector(pvector); } @@ -300,7 +300,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector & void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector2D &pvector) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_double(pvector.X); sb.write_double(pvector.Y); @@ -310,7 +310,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); - CheckNotEmpty(sb); + if (NotInitialized(sb)) return; sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); sb.write_bool(pbool); } @@ -327,7 +327,11 @@ 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) FatalBlueprintError(TEXT("expected lua to return a string")); + if (tag != SimpleDynamicTag::STRING) + { + UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a string")); + return TEXT(""); + } return sb.read_fstring(); } @@ -335,7 +339,11 @@ 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) FatalBlueprintError(TEXT("expected lua to return a name")); + if (tag != SimpleDynamicTag::TOKEN) + { + UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a name")); + return FName(); + } return sb.read_fname(); } @@ -343,7 +351,11 @@ 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) FatalBlueprintError(TEXT("expected lua to return a float")); + if (tag != SimpleDynamicTag::NUMBER) + { + UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a float")); + return 0.0; + } return sb.read_double(); } @@ -351,7 +363,11 @@ 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) FatalBlueprintError(TEXT("expected lua to return a number")); + if (tag != SimpleDynamicTag::NUMBER) + { + UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a number")); + return 0; + } return int(sb.read_double()); } @@ -359,7 +375,11 @@ 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) FatalBlueprintError(TEXT("expected lua to return a vector")); + if (tag != SimpleDynamicTag::VECTOR) + { + UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a vector")); + return FVector(); + } return sb.read_fvector(); } @@ -367,8 +387,12 @@ 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) FatalBlueprintError(TEXT("expected lua to return a vector")); - FVector v = sb.read_fvector(); + 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); } @@ -376,7 +400,169 @@ 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) FatalBlueprintError(TEXT("expected lua to return a boolean")); - return sb.read_bool(); + 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. +// +///////////////////////////////////////////////////////////////// + + +void UlxLuaValues::Empty() +{ + Serialized.clear(); + Types.Empty(); + Data.Empty(); + Cursor = 0; +} + +bool UlxLuaValues::Initialize(std::string_view data) +{ + Empty(); + Serialized = data; + const char *SerializedChar = &Serialized[0]; + + FlxStreamBuffer Decoder(Serialized); + + while (!Decoder.empty()) + { + SimpleDynamicTag Tag = Decoder.read_simple_dynamic_tag(); + + int64 Pos = Decoder.total_reads(); + ElxLuaValueType Type; + switch (Tag) + { + case SimpleDynamicTag::BOOLEAN: Type=ElxLuaValueType::Boolean; Decoder.read_bool(); break; + case SimpleDynamicTag::NUMBER: Type=ElxLuaValueType::Float; Decoder.read_double(); break; + case SimpleDynamicTag::STRING: Type=ElxLuaValueType::String; Decoder.read_string_view(); break; + case SimpleDynamicTag::TOKEN: Type=ElxLuaValueType::Name; Decoder.read_string_view(); break; + case SimpleDynamicTag::VECTOR: Type=ElxLuaValueType::Vector; Decoder.read_fvector(); break; + default: { + Empty(); + return false; + } + } + int64 Pos2 = Decoder.total_reads(); + Types.Add(Type); + Data.Add(std::string_view(SerializedChar + Pos, Pos2 - Pos)); + } + return true; +} + +FString UlxLuaValues::DebugString() const +{ + TStringBuilder<2048> Output; + Output << TEXT("{ "); + for (int i = 0; i < Types.Num(); i++) + { + if (i > 0) Output << TEXT(", "); + + ElxLuaValueType Type = Types[i]; + FlxStreamBuffer Decoder(Data[i]); + switch (Type) + { + case ElxLuaValueType::Boolean: + Output << Decoder.read_bool(); + break; + case ElxLuaValueType::Float: + Output << FString::Printf(TEXT("%lf"), Decoder.read_double()); + break; + case ElxLuaValueType::String: + Output << TEXT("\"") << Decoder.read_fstring() << TEXT("\""); + break; + case ElxLuaValueType::Name: + Output << Decoder.read_fname(); + break; + case ElxLuaValueType::Vector: { + FVector Vec = Decoder.read_fvector(); + Output << FString::Printf(TEXT("(%lf, %lf, %lf)"), Vec.X, Vec.Y, Vec.Z); + break; + } + } + } + Output << TEXT(" }"); + return Output.ToString(); +} + +bool UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) +{ + if (Type != Desired) + { + FString TypeName = StaticEnum()->GetDisplayNameTextByValue(int64(Type)).ToString(); + FString DesiredName = StaticEnum()->GetDisplayNameTextByValue(int64(Desired)).ToString(); + UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName); + return false; + } + return true; +} + +ElxLuaValueType UlxLuaValues::GetType(int n) const +{ + if (n < 0) return ElxLuaValueType::None; + if (n >= Types.Num()) return ElxLuaValueType::None; + return Types[Cursor]; +} + +// +// Get the Nth value in the array. Array bounds-checking +// is handled by 'GetType', which returns 'None' for out-of-bounds +// accesses. +// + +FString UlxLuaValues::GetString(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::String)) return FString(); + FlxStreamBuffer Decoder(Data[n]); + return Decoder.read_fstring(); +} + +FName UlxLuaValues::GetName(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::Name)) return FName(); + FlxStreamBuffer Decoder(Data[n]); + return Decoder.read_fname(); +} + +double UlxLuaValues::GetFloat(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0.0; + FlxStreamBuffer Decoder(Data[n]); + return Decoder.read_double(); +} + +int UlxLuaValues::GetInt(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0; + FlxStreamBuffer Decoder(Data[n]); + return int(Decoder.read_double()); +} + +FVector UlxLuaValues::GetVector(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector(); + FlxStreamBuffer Decoder(Data[n]); + return Decoder.read_fvector(); +} + +FVector2D UlxLuaValues::GetVector2D(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector2D(); + FlxStreamBuffer Decoder(Data[n]); + FVector v = Decoder.read_fvector(); + return FVector2D(v.X, v.Y); +} + +bool UlxLuaValues::GetBoolean(int n) const +{ + if (!CheckType(GetType(n), ElxLuaValueType::Boolean)) return false; + FlxStreamBuffer Decoder(Data[n]); + return Decoder.read_bool(); } diff --git a/Source/Integration/LuaCall.h b/Source/Integration/LuaCall.h index 64c1db84..339ccfb7 100644 --- a/Source/Integration/LuaCall.h +++ b/Source/Integration/LuaCall.h @@ -176,3 +176,145 @@ public: }; + +///////////////////////////////////////////////////////////////// +// +// These are the types that can actually be packed into +// a serialized buffer. +// +///////////////////////////////////////////////////////////////// + + +UENUM(BlueprintType) +enum class ElxLuaValueType : uint8 { + None, + String, + Name, + Float, + Boolean, + Vector +}; + + +///////////////////////////////////////////////////////////////// +// +// 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 bool 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(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; } + + +private: + // + // Functions that get values from the array, random access. + // + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + ElxLuaValueType GetType(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + bool IsType(int n, ElxLuaValueType Type) const { return GetType(n) == Type; } + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + FString GetString(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + FName GetName(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + double GetFloat(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + int GetInt(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + FVector GetVector(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + FVector2D GetVector2D(int n) const; + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + bool GetBoolean(int n) const; + + // + // Functions that get values from the array using the cursor. + // + +public: + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + ElxLuaValueType NextType() const { return GetType(Cursor); } + + UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") + bool IsNextType(ElxLuaValueType Type) const { return IsType(Cursor, Type); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + FString ReadString() { return GetString(Cursor++); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + FName ReadName() { return GetName(Cursor++); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + double ReadFloat() { return GetFloat(Cursor++); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + int ReadInt() { return GetInt(Cursor++); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + FVector ReadVector() { return GetVector(Cursor++); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + FVector2D ReadVector2D() { return GetVector2D(Cursor++); } + + UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") + bool ReadBoolean() { return GetBoolean(Cursor++); } +}; \ No newline at end of file diff --git a/Source/Integration/LuprexGameModeBase.cpp b/Source/Integration/LuprexGameModeBase.cpp index c80b5286..c17de555 100644 --- a/Source/Integration/LuprexGameModeBase.cpp +++ b/Source/Integration/LuprexGameModeBase.cpp @@ -7,6 +7,7 @@ #include "Tangible.h" #include "TangibleManager.h" #include "Blueprint/UserWidget.h" +#include "Kismet/GameplayStatics.h" #include "CommonTypes.h" #include "AnimQueue.h" @@ -355,21 +356,46 @@ void ALuprexGameModeBase::ClearLookAtWidget(const UObject *Context) } } -void ALuprexGameModeBase::SetLookAtWidget(const UObject *Context, UUserWidget *Widget, int ZOrder) +void ALuprexGameModeBase::SetLookAtWidget(const UObject *Context, UlxLookAtWidget *Widget) { ALuprexGameModeBase *Mode = FromContext(Context); if (Mode->LookAtWidget != Widget) { ClearLookAtWidget(Context); } - if (Widget != nullptr) + if (!Widget->IsInViewport()) { - Widget->RemoveFromParent(); - Widget->AddToViewport(ZOrder); + Widget->AddToViewport(100); } Mode->LookAtWidget = Widget; } +void ALuprexGameModeBase::SetLookAt(const UObject *Context, const FHitResult &HitResult) +{ + ALuprexGameModeBase *Mode = FromContext(Context); + Mode->CurrentLookAt = HitResult; +} + +FVector2D ALuprexGameModeBase::GetLookAtPixel(const UObject *Context) +{ + ALuprexGameModeBase *Mode = FromContext(Context); + APlayerController *pc = Context->GetWorld()->GetFirstPlayerController(); + if (pc == nullptr) return FVector2D(); + FVector2D ScreenPosition; + UGameplayStatics::ProjectWorldToScreen(pc, Mode->CurrentLookAt.Location, ScreenPosition, false); + return ScreenPosition; +} + +FVector2D ALuprexGameModeBase::GetPreviousLookAtPixel(const UObject *Context) +{ + ALuprexGameModeBase *Mode = FromContext(Context); + APlayerController *pc = Context->GetWorld()->GetFirstPlayerController(); + if (pc == nullptr) return FVector2D(); + FVector2D ScreenPosition; + UGameplayStatics::ProjectWorldToScreen(pc, Mode->PreviousLookAt.Location, ScreenPosition, false); + return ScreenPosition; +} + void ALuprexGameModeBase::UpdateLookAt() { // Rotate the variables. PreviousLookAt = CurrentLookAt; diff --git a/Source/Integration/LuprexGameModeBase.h b/Source/Integration/LuprexGameModeBase.h index 44d8a855..e637ba13 100644 --- a/Source/Integration/LuprexGameModeBase.h +++ b/Source/Integration/LuprexGameModeBase.h @@ -12,6 +12,7 @@ #include "LuprexSockets.h" #include "TriggeredTask.h" #include "BlueprintErrors.h" +#include "Blueprint/UserWidget.h" #include "LuprexGameModeBase.generated.h" // Messages that come from inside the Luprex Core. @@ -20,12 +21,23 @@ DECLARE_LOG_CATEGORY_EXTERN(LogLuprex, Display, All); // Messages that pertain to our Luprex integration with Unreal. DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Display, All); -class LookAtDetector; +class UlxLuaValues; + + +UCLASS(BlueprintType) +class INTEGRATION_API UlxLookAtWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Luprex|Look-At Detection") + void ReadLuaConfiguration(AActor *Place, UlxLuaValues *Config); +}; /** * */ -UCLASS() +UCLASS(BlueprintType) class INTEGRATION_API ALuprexGameModeBase : public AGameModeBase, public FRunnable { GENERATED_BODY() @@ -56,28 +68,34 @@ public: int64 GetPlayerId(); UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection") - static void SetLookAt(const UObject *Context, const FHitResult &hit) { FromContext(Context)->CurrentLookAt = hit; } - + static void SetLookAt(const UObject *Context, const FHitResult &HitResult); + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") static const FHitResult &GetLookAt(const UObject *Context) { return FromContext(Context)->CurrentLookAt; } UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") static const AActor *GetLookAtActor(const UObject *Context) { return FromContext(Context)->CurrentLookAt.GetActor(); } + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") + static FVector2D GetLookAtPixel(const UObject *Context); + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") static const FHitResult &GetPreviousLookAt(const UObject *Context) { return FromContext(Context)->PreviousLookAt; } UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") static const AActor *GetPreviousLookAtActor(const UObject *Context) { return FromContext(Context)->PreviousLookAt.GetActor(); } + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") + static FVector2D GetPreviousLookAtPixel(const UObject *Context); + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") static bool IsLookAtChanged(const UObject *Context); UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection") - static void SetLookAtWidget(const UObject *Context, UUserWidget *Widget, int ZOrder = 100); + static void SetLookAtWidget(const UObject *Context, UlxLookAtWidget *Widget); UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection") - static UUserWidget *GetLookAtWidget(const UObject *Context) { return FromContext(Context)->LookAtWidget; } + static UlxLookAtWidget *GetLookAtWidget(const UObject *Context) { return FromContext(Context)->LookAtWidget; } UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection") static void ClearLookAtWidget(const UObject *Context); @@ -160,7 +178,7 @@ public: FHitResult CurrentLookAt; UPROPERTY() - UUserWidget *LookAtWidget; + UlxLookAtWidget *LookAtWidget; // The sensitivity level at which a log message triggers a debugger breakpoint. UPROPERTY(EditAnywhere, Category="Debugging Tools") diff --git a/Source/Integration/StringDecoder.cpp b/Source/Integration/StringDecoder.cpp index 19415d28..b5390bca 100644 --- a/Source/Integration/StringDecoder.cpp +++ b/Source/Integration/StringDecoder.cpp @@ -1,154 +1,3 @@ #include "StringDecoder.h" -void UlxLuaValues::Empty() -{ - Serialized.clear(); - Types.Empty(); - Data.Empty(); - Cursor = 0; -} - -bool UlxLuaValues::Initialize(std::string_view data) -{ - Empty(); - Serialized = data; - const char *SerializedChar = &Serialized[0]; - - FlxStreamBuffer Decoder(Serialized); - - while (!Decoder.empty()) - { - SimpleDynamicTag Tag = Decoder.read_simple_dynamic_tag(); - - int64 Pos = Decoder.total_reads(); - ElxLuaValueType Type; - switch (Tag) - { - case SimpleDynamicTag::BOOLEAN: Type=ElxLuaValueType::Boolean; Decoder.read_bool(); break; - case SimpleDynamicTag::NUMBER: Type=ElxLuaValueType::Float; Decoder.read_double(); break; - case SimpleDynamicTag::STRING: Type=ElxLuaValueType::String; Decoder.read_string_view(); break; - case SimpleDynamicTag::TOKEN: Type=ElxLuaValueType::Name; Decoder.read_string_view(); break; - case SimpleDynamicTag::VECTOR: Type=ElxLuaValueType::Vector; Decoder.read_fvector(); break; - default: { - Empty(); - return false; - } - } - int64 Pos2 = Decoder.total_reads(); - Types.Add(Type); - Data.Add(std::string_view(SerializedChar + Pos, Pos2 - Pos)); - } - return true; -} - -FString UlxLuaValues::DebugString() const -{ - TStringBuilder<2048> Output; - Output << TEXT("{ "); - for (int i = 0; i < Types.Num(); i++) - { - if (i > 0) Output << TEXT(", "); - - ElxLuaValueType Type = Types[i]; - FlxStreamBuffer Decoder(Data[i]); - switch (Type) - { - case ElxLuaValueType::Boolean: - Output << Decoder.read_bool(); - break; - case ElxLuaValueType::Float: - Output << FString::Printf(TEXT("%lf"), Decoder.read_double()); - break; - case ElxLuaValueType::String: - Output << TEXT("\"") << Decoder.read_fstring() << TEXT("\""); - break; - case ElxLuaValueType::Name: - Output << Decoder.read_fname(); - break; - case ElxLuaValueType::Vector: { - FVector Vec = Decoder.read_fvector(); - Output << FString::Printf(TEXT("(%lf, %lf, %lf)"), Vec.X, Vec.Y, Vec.Z); - break; - } - } - } - Output << TEXT(" }"); - return Output.ToString(); -} - -bool UlxLuaValues::CheckType(ElxLuaValueType Type, ElxLuaValueType Desired) -{ - if (Type != Desired) - { - FString TypeName = StaticEnum()->GetDisplayNameTextByValue(int64(Type)).ToString(); - FString DesiredName = StaticEnum()->GetDisplayNameTextByValue(int64(Desired)).ToString(); - UE_LOG(LogBlueprint, Error, TEXT("Expected a value of type %s, but found %s instead."), *DesiredName, *TypeName); - return false; - } - return true; -} - -ElxLuaValueType UlxLuaValues::GetType(int n) const -{ - if (n < 0) return ElxLuaValueType::None; - if (n >= Types.Num()) return ElxLuaValueType::None; - return Types[Cursor]; -} - -// -// Get the Nth value in the array. Array bounds-checking -// is handled by 'GetType', which returns 'None' for out-of-bounds -// accesses. -// - -FString UlxLuaValues::GetString(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::String)) return FString(); - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_fstring(); -} - -FName UlxLuaValues::GetName(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::Name)) return FName(); - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_fname(); -} - -double UlxLuaValues::GetFloat(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0.0; - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_double(); -} - -int UlxLuaValues::GetInt(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::Float)) return 0; - FlxStreamBuffer Decoder(Data[n]); - return int(Decoder.read_double()); -} - -FVector UlxLuaValues::GetVector(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector(); - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_fvector(); -} - -FVector2D UlxLuaValues::GetVector2D(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::Vector)) return FVector2D(); - FlxStreamBuffer Decoder(Data[n]); - FVector v = Decoder.read_fvector(); - return FVector2D(v.X, v.Y); -} - -bool UlxLuaValues::GetBoolean(int n) const -{ - if (!CheckType(GetType(n), ElxLuaValueType::Boolean)) return false; - FlxStreamBuffer Decoder(Data[n]); - return Decoder.read_bool(); -} - diff --git a/Source/Integration/StringDecoder.h b/Source/Integration/StringDecoder.h index e3819e9c..f7da2558 100644 --- a/Source/Integration/StringDecoder.h +++ b/Source/Integration/StringDecoder.h @@ -3,8 +3,6 @@ #include "CoreMinimal.h" #include "lpx-basebuffer.hpp" -#include "StringDecoder.generated.h" - class FlxStreamBufferCore { private: bool err_eof_on_read_; @@ -68,144 +66,3 @@ public: return FName(s.size(), (const UTF8CHAR *)s.data(), FNAME_Add); } }; - -///////////////////////////////////////////////////////////////// -// -// These are the types that can actually be packed into -// a serialized buffer. -// -///////////////////////////////////////////////////////////////// - -UENUM(BlueprintType) -enum class ElxLuaValueType : uint8 { - None, - String, - Name, - Float, - Boolean, - Vector -}; - - -///////////////////////////////////////////////////////////////// -// -// This class stores an array of values that were returned by Lua. -// -///////////////////////////////////////////////////////////////// - -UCLASS(MinimalAPI) -class 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 bool 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(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; } - - -private: - // - // Functions that get values from the array, random access. - // - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - ElxLuaValueType GetType(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool IsType(int n, ElxLuaValueType Type) const { return GetType(n) == Type; } - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FString GetString(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FName GetName(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - double GetFloat(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - int GetInt(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FVector GetVector(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - FVector2D GetVector2D(int n) const; - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool GetBoolean(int n) const; - - // - // Functions that get values from the array using the cursor. - // - -public: - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - ElxLuaValueType NextType() const { return GetType(Cursor); } - - UFUNCTION(BlueprintPure, Category = "Luprex|Lua Value Array") - bool IsNextType(ElxLuaValueType Type) const { return IsType(Cursor, Type); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FString ReadString() { return GetString(Cursor++); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FName ReadName() { return GetName(Cursor++); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - double ReadFloat() { return GetFloat(Cursor++); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - int ReadInt() { return GetInt(Cursor++); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FVector ReadVector() { return GetVector(Cursor++); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - FVector2D ReadVector2D() { return GetVector2D(Cursor++); } - - UFUNCTION(BlueprintCallable, Category = "Luprex|Lua Value Array") - bool ReadBoolean() { return GetBoolean(Cursor++); } -}; \ No newline at end of file diff --git a/Source/Integration/UtilityLibrary.h b/Source/Integration/UtilityLibrary.h index 44beab25..abb760bf 100644 --- a/Source/Integration/UtilityLibrary.h +++ b/Source/Integration/UtilityLibrary.h @@ -77,6 +77,9 @@ public: // Be aware that (0.0, 0.0) is the upper-left corner of the upper-left pixel, // whereas (0.5, 0.5) is the center of the upper-left pixel. // + // The resulting TraceStart and TraceEnd fields of the HitResult will not + // contain world positions, instead, they will contain the PixelXY. + // UFUNCTION(BlueprintCallable, Category="Collision", meta=(AutoCreateRefTerm="ActorsToIgnore", Keywords="raycast")) static bool LineTraceThroughPixel(const APlayerController* PlayerController, FVector2D PixelXY, double MaxDistanceFromCamera, diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 32be1723..18c50c44 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -417,7 +417,7 @@ eng::string World::probe_lua_expr(int64_t actor_id, std::string_view lua) { return result; } -void World::probe_lua_call(int64_t place_id, int64_t actor_id, std::string_view datapack, StreamBuffer *retvals) { +void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view datapack, StreamBuffer *retvals) { assert(stack_is_clear()); lua_State *L = state(); diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index fdc44b72..1de42167 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -249,7 +249,7 @@ public: // Print statements are discarded. The lua function may return a vector // of values. If so, the values are packed into a StreamBuffer. // - void probe_lua_call(int64_t place_id, int64_t actor_id, std::string_view datapack, StreamBuffer *retvals); + void probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view datapack, StreamBuffer *retvals); // Invoke an Invocation object. // diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index c4851b7f..17310b25 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -18,11 +18,17 @@ function engio.move(action, xyz, facing) tangible.animate(actor, nil, {action=action, xyz=xyz, facing=facing}) end -function engio.printhi(a1, a2, a3, a4, a5) - pprint("Hi there", a1, a2, a3, a4, a5) +function cube.getlookat() + return { "Hotkeys", "X", "I Am a Cube" } end -function engio.retmany() - return 7, vec(8,9,10), "Yo", "Banana", 13.2, vec(2,3,4), "Hi" +function sphere.getlookat() + return { "Hotkeys", "X", "I Am a Sphere" } +end + +function engio.getlookat() + local place = tangible.place() + local class = tangible.getclass(place) + return class.getlookat() end