Lots of work on look-at widgets

This commit is contained in:
2025-04-07 16:48:27 -04:00
parent e81134473e
commit d35125eb70
24 changed files with 554 additions and 379 deletions

Binary file not shown.

Binary file not shown.

BIN
Content/Widgets/WB_Console.uasset LFS Normal file

Binary file not shown.

Binary file not shown.

BIN
Content/Widgets/WB_Hotkeys.uasset LFS Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -4,6 +4,9 @@
#include "AssetRegistry/AssetData.h" #include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryState.h" #include "AssetRegistry/AssetRegistryState.h"
#include "LuprexGameModeBase.h" #include "LuprexGameModeBase.h"
#include "Components/Widget.h"
#include "WidgetBlueprint.h"
#include "Blueprint/UserWidget.h"
void UlxAssetLookup::RebuildIndex() void UlxAssetLookup::RebuildIndex()
{ {
@@ -12,6 +15,7 @@ void UlxAssetLookup::RebuildIndex()
IAssetRegistry::GetChecked().WaitForCompletion(); IAssetRegistry::GetChecked().WaitForCompletion();
ScanTangibles(); ScanTangibles();
ScanStaticMeshes(); ScanStaticMeshes();
ScanWidgets();
} }
void UlxAssetLookup::ScanTangibles() void UlxAssetLookup::ScanTangibles()
@@ -27,7 +31,7 @@ void UlxAssetLookup::ScanTangibles()
UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d assets in /Game/Tangibles"), FoundData.Num()); UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d assets in /Game/Tangibles"), FoundData.Num());
for (const FAssetData &Data : FoundData) for (const FAssetData &Data : FoundData)
{ {
FString Path = Data.GetObjectPathString(); FString Path = Data.GetObjectPathString() + TEXT("_C");
CachedTangibles.Add(Data.AssetName, Path); CachedTangibles.Add(Data.AssetName, Path);
} }
} }
@@ -42,7 +46,7 @@ void UlxAssetLookup::ScanStaticMeshes()
AssetFilter.bRecursivePaths = true; AssetFilter.bRecursivePaths = true;
IAssetRegistry::GetChecked().GetAssets(AssetFilter, FoundData); 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) for (const FAssetData &Data : FoundData)
{ {
FString Path = Data.GetObjectPathString(); FString Path = Data.GetObjectPathString();
@@ -50,10 +54,26 @@ void UlxAssetLookup::ScanStaticMeshes()
} }
} }
void UlxAssetLookup::ScanWidgets()
{
TArray<FAssetData> 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 FString UlxAssetLookup::TangibleLoadPath(const FName &AssetName) const
{ {
FScopeLock lock(&Mutex);
const FString *Result = CachedTangibles.Find(AssetName); const FString *Result = CachedTangibles.Find(AssetName);
if (Result == nullptr) return TEXT(""); if (Result == nullptr) return TEXT("");
return *Result; return *Result;
@@ -61,13 +81,18 @@ FString UlxAssetLookup::TangibleLoadPath(const FName &AssetName) const
FString UlxAssetLookup::StaticMeshLoadPath(const FName &AssetName) const FString UlxAssetLookup::StaticMeshLoadPath(const FName &AssetName) const
{ {
FScopeLock lock(&Mutex);
const FString *Result = CachedStaticMeshes.Find(AssetName); const FString *Result = CachedStaticMeshes.Find(AssetName);
if (Result == nullptr) return TEXT(""); if (Result == nullptr) return TEXT("");
return *Result; 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) UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const FString &Name)
{ {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); 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); UE_LOG(LogLuprexIntegration, Error, TEXT("Static mesh not on search path: %s"), *Name);
return nullptr; return nullptr;
} }
UStaticMesh *Result = LoadObject<UStaticMesh>(nullptr, *Path);
FString FullPath = Path;
UStaticMesh *Result = LoadObject<UStaticMesh>(nullptr, *FullPath);
if (Result == nullptr) { 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 nullptr;
} }
return Result; return Result;
} }
UClass *UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name) { TSubclassOf<AActor> UlxAssetLookup::GetTangibleClassByName(const UObject *Context, const FString &Name) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context);
FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name)); FString Path = mode->GetAssetLookup()->TangibleLoadPath(FName(FString("TAN_") + Name));
if (Path.IsEmpty()) 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); UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible not on search path: %s"), *Name);
return nullptr; return nullptr;
} }
FString FullPath = Path + TEXT("_C"); UClass *Result = LoadObject<UClass>(nullptr, *Path);
UClass *Result = LoadObject<UClass>(nullptr, *FullPath);
if (Result == nullptr) { 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; return nullptr;
} }
if (!Result->IsChildOf(AActor::StaticClass())) { 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; return nullptr;
} }
UFunction *aqchanged = Result->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); UFunction *aqchanged = Result->FindFunctionByName(FName(TEXT("Animation Queue Changed")));
if ((aqchanged == nullptr)||(aqchanged->ParmsSize != 0)) 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 nullptr;
} }
return Result; return Result;
} }
TSubclassOf<UUserWidget> 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<UClass>(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<UlxLookAtWidget> 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<UClass>(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;
}

View File

@@ -6,14 +6,17 @@
#include "CommonTypes.h" #include "CommonTypes.h"
#include "AssetLookup.generated.h" #include "AssetLookup.generated.h"
class AActor;
class UUserWidget;
class UlxLookAtWidget;
class UStaticMesh;
UCLASS(MinimalAPI) UCLASS(MinimalAPI)
class UlxAssetLookup : public UObject class UlxAssetLookup : public UObject
{ {
GENERATED_BODY() GENERATED_BODY()
private: private:
mutable FCriticalSection Mutex;
// Map from asset name to full loadable path. // Map from asset name to full loadable path.
UPROPERTY() UPROPERTY()
TMap<FName, FString> CachedTangibles; TMap<FName, FString> CachedTangibles;
@@ -22,22 +25,40 @@ private:
UPROPERTY() UPROPERTY()
TMap<FName, FString> CachedStaticMeshes; TMap<FName, FString> CachedStaticMeshes;
public: // Map from asset name to full loadable path.
void RebuildIndex(); UPROPERTY()
TMap<FName, FString> CachedWidgets;
private:
void ScanTangibles(); void ScanTangibles();
void ScanStaticMeshes(); 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; 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; 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 // Get a static mesh by name
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous")
static UStaticMesh *GetStaticMeshByName(const UObject *Context, const FString &Name); 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") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous")
static UClass *GetTangibleClassByName(const UObject *Context, const FString &Name); static TSubclassOf<AActor> GetTangibleClassByName(const UObject *Context, const FString &Name);
// Get a widget blueprint by name
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous")
static TSubclassOf<UUserWidget> 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<UlxLookAtWidget> GetLookAtWidgetByName(const UObject *Context, const FString &Name);
}; };

View File

@@ -1,6 +1,6 @@
#include "BlueprintErrors.h" #include "BlueprintErrors.h"
#include "StringDecoder.h" #include "LuaCall.h"
#include "Internationalization/TextFormatter.h" #include "Internationalization/TextFormatter.h"
#include "Kismet/KismetSystemLibrary.h" #include "Kismet/KismetSystemLibrary.h"
#include "Kismet2/KismetDebugUtilities.h" #include "Kismet2/KismetDebugUtilities.h"

View File

@@ -25,6 +25,7 @@ public class Integration : ModuleRules
"KismetWidgets", "KismetWidgets",
"BlueprintGraph", "BlueprintGraph",
"UMG", "UMG",
"UMGEditor",
}); });
// Uncomment if you are using Slate UI // Uncomment if you are using Slate UI

View File

@@ -5,15 +5,14 @@
#include "EdGraphSchema_K2.h" #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) { static bool NotInitialized(const FlxStreamBuffer &sb) {
if (sb.empty()) { if (sb.empty())
FatalBlueprintError(TEXT("Must use LuaCallBegin before other LuaCall steps")); {
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) { 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); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
mode->LuaCallEnd(InvocationKind::LUA_INVOKE, place); mode->LuaCallEnd(InvocationKind::LUA_INVOKE, place);
} }
@@ -228,7 +227,7 @@ void UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place)
{ {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
mode->LuaCallEnd(InvocationKind::LUA_PROBE, place); mode->LuaCallEnd(InvocationKind::LUA_PROBE, place);
} }
@@ -254,7 +253,7 @@ UlxLuaValues *UlxLuaCallLibrary::LuaCallGetRest(UObject *context)
void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) { void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING); sb.write_simple_dynamic_tag(SimpleDynamicTag::STRING);
sb.write_string(pstring); sb.write_string(pstring);
} }
@@ -263,12 +262,13 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam
FTCHARToUTF8 utf8str(pname.ToString()); FTCHARToUTF8 utf8str(pname.ToString());
std::string_view namestr(utf8str.Get(), utf8str.Length()); std::string_view namestr(utf8str.Get(), utf8str.Length());
uint64_t tokvalue = ParseNameAsToken(namestr); uint64_t tokvalue = ParseNameAsToken(namestr);
if ((tokvalue == 0) && !namestr.empty()) { if ((tokvalue == 0) && !namestr.empty())
FatalBlueprintError(TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); {
UE_LOG(LogBlueprint, Error, TEXT("Names passed to lua must be short, and must contain only lowercase and digits"));
} }
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN); sb.write_simple_dynamic_tag(SimpleDynamicTag::TOKEN);
sb.write_string(namestr); 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) { void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
sb.write_double(pfloat); sb.write_double(pfloat);
} }
@@ -284,7 +284,7 @@ void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) {
void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); sb.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
sb.write_double(value); 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) { void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &pvector) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR);
sb.write_fvector(pvector); 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) { void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector2D &pvector) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); sb.write_simple_dynamic_tag(SimpleDynamicTag::VECTOR);
sb.write_double(pvector.X); sb.write_double(pvector.X);
sb.write_double(pvector.Y); 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) { void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetBuffer(); FlxStreamBuffer &sb = mode->LuaCallGetBuffer();
CheckNotEmpty(sb); if (NotInitialized(sb)) return;
sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); sb.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN);
sb.write_bool(pbool); sb.write_bool(pbool);
} }
@@ -327,7 +327,11 @@ FString UlxLuaCallLibrary::LuaCallReturnValue_string(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); 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(); return sb.read_fstring();
} }
@@ -335,7 +339,11 @@ FName UlxLuaCallLibrary::LuaCallReturnValue_name(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); 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(); return sb.read_fname();
} }
@@ -343,7 +351,11 @@ double UlxLuaCallLibrary::LuaCallReturnValue_float(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); 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(); return sb.read_double();
} }
@@ -351,7 +363,11 @@ int UlxLuaCallLibrary::LuaCallReturnValue_int(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); 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()); return int(sb.read_double());
} }
@@ -359,7 +375,11 @@ FVector UlxLuaCallLibrary::LuaCallReturnValue_vector(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); 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(); return sb.read_fvector();
} }
@@ -367,8 +387,12 @@ FVector2D UlxLuaCallLibrary::LuaCallReturnValue_vector2d(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag();
if (tag != SimpleDynamicTag::VECTOR) FatalBlueprintError(TEXT("expected lua to return a vector")); if (tag != SimpleDynamicTag::VECTOR)
FVector v = sb.read_fvector(); {
UE_LOG(LogBlueprint, Error, TEXT("expected lua to return a vector"));
return FVector2D();
}
FVector v = sb.read_fvector();
return FVector2D(v.X, v.Y); return FVector2D(v.X, v.Y);
} }
@@ -376,7 +400,169 @@ bool UlxLuaCallLibrary::LuaCallReturnValue_boolean(UObject *context) {
ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context);
FlxStreamBuffer &sb = mode->LuaCallGetResult(); FlxStreamBuffer &sb = mode->LuaCallGetResult();
SimpleDynamicTag tag = sb.read_simple_dynamic_tag(); SimpleDynamicTag tag = sb.read_simple_dynamic_tag();
if (tag != SimpleDynamicTag::BOOLEAN) FatalBlueprintError(TEXT("expected lua to return a boolean")); if (tag != SimpleDynamicTag::BOOLEAN)
return sb.read_bool(); {
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<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString();
FString DesiredName = StaticEnum<ElxLuaValueType>()->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();
} }

View File

@@ -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<ElxLuaValueType> Types;
// For each chunk, a pointer to the serialized data.
//
TArray<std::string_view> 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++); }
};

View File

@@ -7,6 +7,7 @@
#include "Tangible.h" #include "Tangible.h"
#include "TangibleManager.h" #include "TangibleManager.h"
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
#include "Kismet/GameplayStatics.h"
#include "CommonTypes.h" #include "CommonTypes.h"
#include "AnimQueue.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); ALuprexGameModeBase *Mode = FromContext(Context);
if (Mode->LookAtWidget != Widget) if (Mode->LookAtWidget != Widget)
{ {
ClearLookAtWidget(Context); ClearLookAtWidget(Context);
} }
if (Widget != nullptr) if (!Widget->IsInViewport())
{ {
Widget->RemoveFromParent(); Widget->AddToViewport(100);
Widget->AddToViewport(ZOrder);
} }
Mode->LookAtWidget = Widget; 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() { void ALuprexGameModeBase::UpdateLookAt() {
// Rotate the variables. // Rotate the variables.
PreviousLookAt = CurrentLookAt; PreviousLookAt = CurrentLookAt;

View File

@@ -12,6 +12,7 @@
#include "LuprexSockets.h" #include "LuprexSockets.h"
#include "TriggeredTask.h" #include "TriggeredTask.h"
#include "BlueprintErrors.h" #include "BlueprintErrors.h"
#include "Blueprint/UserWidget.h"
#include "LuprexGameModeBase.generated.h" #include "LuprexGameModeBase.generated.h"
// Messages that come from inside the Luprex Core. // 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. // Messages that pertain to our Luprex integration with Unreal.
DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Display, All); 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 class INTEGRATION_API ALuprexGameModeBase : public AGameModeBase, public FRunnable
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -56,7 +68,7 @@ public:
int64 GetPlayerId(); int64 GetPlayerId();
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection") 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") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection")
static const FHitResult &GetLookAt(const UObject *Context) { return FromContext(Context)->CurrentLookAt; } static const FHitResult &GetLookAt(const UObject *Context) { return FromContext(Context)->CurrentLookAt; }
@@ -64,20 +76,26 @@ public:
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection")
static const AActor *GetLookAtActor(const UObject *Context) { return FromContext(Context)->CurrentLookAt.GetActor(); } 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") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection")
static const FHitResult &GetPreviousLookAt(const UObject *Context) { return FromContext(Context)->PreviousLookAt; } static const FHitResult &GetPreviousLookAt(const UObject *Context) { return FromContext(Context)->PreviousLookAt; }
UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection")
static const AActor *GetPreviousLookAtActor(const UObject *Context) { return FromContext(Context)->PreviousLookAt.GetActor(); } 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") UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"),Category = "Luprex|Look-At Detection")
static bool IsLookAtChanged(const UObject *Context); static bool IsLookAtChanged(const UObject *Context);
UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection") 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") 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") UFUNCTION(BlueprintCallable, meta = (WorldContext = "Context"), Category = "Luprex|Look-At Detection")
static void ClearLookAtWidget(const UObject *Context); static void ClearLookAtWidget(const UObject *Context);
@@ -160,7 +178,7 @@ public:
FHitResult CurrentLookAt; FHitResult CurrentLookAt;
UPROPERTY() UPROPERTY()
UUserWidget *LookAtWidget; UlxLookAtWidget *LookAtWidget;
// The sensitivity level at which a log message triggers a debugger breakpoint. // The sensitivity level at which a log message triggers a debugger breakpoint.
UPROPERTY(EditAnywhere, Category="Debugging Tools") UPROPERTY(EditAnywhere, Category="Debugging Tools")

View File

@@ -1,154 +1,3 @@
#include "StringDecoder.h" #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<ElxLuaValueType>()->GetDisplayNameTextByValue(int64(Type)).ToString();
FString DesiredName = StaticEnum<ElxLuaValueType>()->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();
}

View File

@@ -3,8 +3,6 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "lpx-basebuffer.hpp" #include "lpx-basebuffer.hpp"
#include "StringDecoder.generated.h"
class FlxStreamBufferCore { class FlxStreamBufferCore {
private: private:
bool err_eof_on_read_; bool err_eof_on_read_;
@@ -68,144 +66,3 @@ public:
return FName(s.size(), (const UTF8CHAR *)s.data(), FNAME_Add); 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<ElxLuaValueType> Types;
// For each chunk, a pointer to the serialized data.
//
TArray<std::string_view> 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++); }
};

View File

@@ -77,6 +77,9 @@ public:
// Be aware that (0.0, 0.0) is the upper-left corner of the upper-left pixel, // 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. // 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")) UFUNCTION(BlueprintCallable, Category="Collision", meta=(AutoCreateRefTerm="ActorsToIgnore", Keywords="raycast"))
static bool LineTraceThroughPixel(const APlayerController* PlayerController, static bool LineTraceThroughPixel(const APlayerController* PlayerController,
FVector2D PixelXY, double MaxDistanceFromCamera, FVector2D PixelXY, double MaxDistanceFromCamera,

View File

@@ -417,7 +417,7 @@ eng::string World::probe_lua_expr(int64_t actor_id, std::string_view lua) {
return result; 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()); assert(stack_is_clear());
lua_State *L = state(); lua_State *L = state();

View File

@@ -249,7 +249,7 @@ public:
// Print statements are discarded. The lua function may return a vector // Print statements are discarded. The lua function may return a vector
// of values. If so, the values are packed into a StreamBuffer. // 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. // Invoke an Invocation object.
// //

View File

@@ -18,11 +18,17 @@ function engio.move(action, xyz, facing)
tangible.animate(actor, nil, {action=action, xyz=xyz, facing=facing}) tangible.animate(actor, nil, {action=action, xyz=xyz, facing=facing})
end end
function engio.printhi(a1, a2, a3, a4, a5) function cube.getlookat()
pprint("Hi there", a1, a2, a3, a4, a5) return { "Hotkeys", "X", "I Am a Cube" }
end end
function engio.retmany() function sphere.getlookat()
return 7, vec(8,9,10), "Yo", "Banana", 13.2, vec(2,3,4), "Hi" return { "Hotkeys", "X", "I Am a Sphere" }
end
function engio.getlookat()
local place = tangible.place()
local class = tangible.getclass(place)
return class.getlookat()
end end