From aa511b9b8c322602c0ad73c1173cf21099ff1185 Mon Sep 17 00:00:00 2001 From: jyelon Date: Tue, 1 Apr 2025 22:31:27 -0400 Subject: [PATCH] Add AssetLookup module to find assets by name. --- .../Mannequins/Animations/ABP_Manny.uasset | 4 +- .../Mannequins/Animations/ABP_Quinn.uasset | 4 +- Content/Tangibles/TanStaticMesh.uasset | 4 +- Source/Integration/AssetLookup.cpp | 117 ++++++++++++++++++ Source/Integration/AssetLookup.h | 43 +++++++ Source/Integration/DebugPrint.h | 52 ++++++++ Source/Integration/LuprexGameModeBase.cpp | 18 ++- Source/Integration/LuprexGameModeBase.h | 13 +- Source/Integration/Tangible.cpp | 6 +- Source/Integration/TangibleManager.cpp | 49 +------- Source/Integration/TangibleManager.h | 18 +-- 11 files changed, 250 insertions(+), 78 deletions(-) create mode 100644 Source/Integration/AssetLookup.cpp create mode 100644 Source/Integration/AssetLookup.h create mode 100644 Source/Integration/DebugPrint.h diff --git a/Content/Characters/Mannequins/Animations/ABP_Manny.uasset b/Content/Characters/Mannequins/Animations/ABP_Manny.uasset index 0a6ce0b1..3ecabfe0 100644 --- a/Content/Characters/Mannequins/Animations/ABP_Manny.uasset +++ b/Content/Characters/Mannequins/Animations/ABP_Manny.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76f29c53966aa4590e50a4eafa70df607f978c75120c359a4dc5c05bafa2262c -size 339660 +oid sha256:7eb00a14ddf91ecf60b4254e1017226dbf182cb10090c725d90185723fb6cd5c +size 340015 diff --git a/Content/Characters/Mannequins/Animations/ABP_Quinn.uasset b/Content/Characters/Mannequins/Animations/ABP_Quinn.uasset index c4c3e9c4..b8a7c9fa 100644 --- a/Content/Characters/Mannequins/Animations/ABP_Quinn.uasset +++ b/Content/Characters/Mannequins/Animations/ABP_Quinn.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:203f61ef3a33812d876c340dfa39965ba5fdc58eafed70ad98390453f43ac322 -size 39506 +oid sha256:8ae29593423c4d88ad4ffba24e012e231a1c93e361ebb611924773472b49ebcc +size 39554 diff --git a/Content/Tangibles/TanStaticMesh.uasset b/Content/Tangibles/TanStaticMesh.uasset index b9e68884..dee965a6 100644 --- a/Content/Tangibles/TanStaticMesh.uasset +++ b/Content/Tangibles/TanStaticMesh.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76c26a80d6928a8d08b4501472364c9ad74b0d228def034f5e2da3d8c9ac1250 -size 207861 +oid sha256:d50bfd75f52c003358616ea192eb17ec277c2b728923b5ad6cefe343aaf655ce +size 169767 diff --git a/Source/Integration/AssetLookup.cpp b/Source/Integration/AssetLookup.cpp new file mode 100644 index 00000000..eb92bdb0 --- /dev/null +++ b/Source/Integration/AssetLookup.cpp @@ -0,0 +1,117 @@ + +#include "AssetLookup.h" +#include "AssetRegistry/IAssetRegistry.h" +#include "AssetRegistry/AssetData.h" +#include "AssetRegistry/AssetRegistryState.h" +#include "LuprexGameModeBase.h" + +void UlxAssetLookup::RebuildIndex() +{ + CachedTangibles.Empty(); + CachedStaticMeshes.Empty(); + IAssetRegistry::GetChecked().WaitForCompletion(); + ScanTangibles(); + ScanStaticMeshes(); +} + +void UlxAssetLookup::ScanTangibles() +{ + TArray FoundData; + FARFilter AssetFilter; + AssetFilter.PackagePaths.Add(FName(TEXT("/Game/Tangibles"))); + AssetFilter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); + AssetFilter.bIncludeOnlyOnDiskAssets = true; + AssetFilter.bRecursivePaths = true; + IAssetRegistry::GetChecked().GetAssets(AssetFilter, FoundData); + + UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d assets in /Game/Tangibles"), FoundData.Num()); + for (const FAssetData &Data : FoundData) + { + FString Path = Data.GetObjectPathString(); + CachedTangibles.Add(Data.AssetName, Path); + } +} + +void UlxAssetLookup::ScanStaticMeshes() +{ + TArray FoundData; + FARFilter AssetFilter; + AssetFilter.PackagePaths.Add(FName(TEXT("/Game/StarterContent/Shapes"))); + AssetFilter.PackagePaths.Add(FName(TEXT("/Game/StaticMeshes"))); + AssetFilter.ClassPaths.Add(UStaticMesh::StaticClass()->GetClassPathName()); + AssetFilter.bIncludeOnlyOnDiskAssets = true; + AssetFilter.bRecursivePaths = true; + IAssetRegistry::GetChecked().GetAssets(AssetFilter, FoundData); + + UE_LOG(LogLuprexIntegration, Display, TEXT("Found %d static mesh assets"), FoundData.Num()); + for (const FAssetData &Data : FoundData) + { + FString Path = Data.GetObjectPathString(); + CachedStaticMeshes.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; +} + +FString UlxAssetLookup::StaticMeshLoadPath(const FName &AssetName) const +{ + FScopeLock lock(&Mutex); + + const FString *Result = CachedStaticMeshes.Find(AssetName); + if (Result == nullptr) return TEXT(""); + return *Result; +} + +UStaticMesh *UlxAssetLookup::GetStaticMeshByName(const UObject *Context, const FString &Name) +{ + ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(Context); + FString Path = mode->GetAssetLookup()->StaticMeshLoadPath(FName(Name)); + if (Path.IsEmpty()) + { + UE_LOG(LogLuprexIntegration, Error, TEXT("Static mesh not on search path: %s"), *Name); + return nullptr; + } + + FString FullPath = Path; + UStaticMesh *Result = LoadObject(nullptr, *FullPath); + if (Result == nullptr) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load static mesh: %s"), *FullPath); + return nullptr; + } + + return Result; +} + +UClass *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()) + { + UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible not on search path: %s"), *Name); + return nullptr; + } + FString FullPath = Path + TEXT("_C"); + UClass *Result = LoadObject(nullptr, *FullPath); + if (Result == nullptr) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Cannot load tangible class: %s"), *FullPath); + return nullptr; + } + if (!Result->IsChildOf(AActor::StaticClass())) { + UE_LOG(LogLuprexIntegration, Error, TEXT("Tangible class is not an actor: %s"), *FullPath); + 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); + return nullptr; + } + return Result; +} diff --git a/Source/Integration/AssetLookup.h b/Source/Integration/AssetLookup.h new file mode 100644 index 00000000..54db6aee --- /dev/null +++ b/Source/Integration/AssetLookup.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "CommonTypes.h" +#include "AssetLookup.generated.h" + +UCLASS(MinimalAPI) +class UlxAssetLookup : public UObject +{ + GENERATED_BODY() + +private: + mutable FCriticalSection Mutex; + + // Map from asset name to full loadable path. + UPROPERTY() + TMap CachedTangibles; + + // Map from asset name to to full loadable path. + UPROPERTY() + TMap CachedStaticMeshes; + +public: + void RebuildIndex(); + void ScanTangibles(); + void ScanStaticMeshes(); + + // Get the full path name of a + FString TangibleLoadPath(const FName &AssetName) const; + + // Get a static mesh by its asset name. + FString StaticMeshLoadPath(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 + UFUNCTION(BlueprintPure, meta = (WorldContext = "Context"), Category = "Luprex|Miscellaneous") + static UClass *GetTangibleClassByName(const UObject *Context, const FString &Name); +}; diff --git a/Source/Integration/DebugPrint.h b/Source/Integration/DebugPrint.h new file mode 100644 index 00000000..c65a2e19 --- /dev/null +++ b/Source/Integration/DebugPrint.h @@ -0,0 +1,52 @@ +#pragma once + +////////////////////////////////////////////////////////////// +// +// ConsoleOutput +// +// This class stores the text that's in the unreal console. +// It stores it as one great big string, which contains +// newlines to denote line breaks. +// +// This class also contains a 'dirty' bit. Each time somebody +// appends a line of text to the console, the dirty bit is +// automatically set. The bit can be checked using 'IsDirty' +// and cleared using 'ClearDirty'. This makes it so that +// you don't have to update the unreal widget unless the +// text has actually changed. +// +////////////////////////////////////////////////////////////// + +class FlxConsoleOutput { +private: + FString Content; + bool Dirty; + + // Truncate the console to a reasonable number of + // lines. The length is hardwired. + void Truncate(); + + // Add a newline if there isn't one. Returns true if it changed anything. + bool MaybeAppendNewline(); + + // Append text. Returns true if it changed anything. + bool MaybeAppendText(const FString& text); + +public: + // Append a line of text to the console. + void Append(const FString& text); + + // Append a line of text to the console on a line by itself. + void AppendLine(const FString& text); + + // Get the console text as a string. + const FString& Get() const { return Content; } + + // Return if the dirty flag is set. + bool IsDirty() const { return Dirty; } + + // Clear the dirty flag. + void ClearDirty() { Dirty = false; } +}; + + diff --git a/Source/Integration/LuprexGameModeBase.cpp b/Source/Integration/LuprexGameModeBase.cpp index 476e759b..46e6140a 100644 --- a/Source/Integration/LuprexGameModeBase.cpp +++ b/Source/Integration/LuprexGameModeBase.cpp @@ -18,7 +18,8 @@ DEFINE_LOG_CATEGORY(LogLuprexIntegration); ALuprexGameModeBase::ALuprexGameModeBase() { - TangibleManager = NewObject(); + TangibleManager = nullptr; + AssetLookup = nullptr; PlayerId = 0; EngineSeconds = 0.0; //PrimaryActorTick.bCanEverTick = true; // Probably wrong @@ -58,6 +59,11 @@ void ALuprexGameModeBase::ResetToInitialState() TangibleManager = nullptr; } + if (AssetLookup != nullptr) { + AssetLookup->ConditionalBeginDestroy(); + AssetLookup = nullptr; + } + // Shut down the thread LuprexUpdateTask.Shutdown(); @@ -301,9 +307,13 @@ void ALuprexGameModeBase::InitializeGlobalState() LuprexUpdateTask.Startup(this); } + // Initialize the asset lookup table. + AssetLookup = NewObject(this); + AssetLookup->RebuildIndex(); + // Initialize the tangible manager. - TangibleManager = NewObject(); - TangibleManager->Init(GetWorld(), this); + TangibleManager = NewObject(this); + TangibleManager->Init(this); // If somebody generates a log message that's severe enough, break to debugger. BreakToDebuggerLogVerbosityDevice.Reset( @@ -319,7 +329,7 @@ int64 ALuprexGameModeBase::GetPlayerId() { return PlayerId; } -ALuprexGameModeBase *ALuprexGameModeBase::FromContext(UObject *context) { +ALuprexGameModeBase *ALuprexGameModeBase::FromContext(const UObject *context) { ALuprexGameModeBase *result = context->GetWorld()->GetAuthGameMode(); if (result == nullptr) { UE_LOG(LogBlueprint, Fatal, TEXT("Not currently using a Luprex Game Mode.")); diff --git a/Source/Integration/LuprexGameModeBase.h b/Source/Integration/LuprexGameModeBase.h index c9dfe895..2402c316 100644 --- a/Source/Integration/LuprexGameModeBase.h +++ b/Source/Integration/LuprexGameModeBase.h @@ -8,16 +8,17 @@ #include "ConsoleOutput.h" #include "StringDecoder.h" #include "TangibleManager.h" +#include "AssetLookup.h" #include "LuprexSockets.h" #include "TriggeredTask.h" #include "BlueprintErrors.h" #include "LuprexGameModeBase.generated.h" // Messages that come from inside the Luprex Core. -DECLARE_LOG_CATEGORY_EXTERN(LogLuprex, Warning, All); +DECLARE_LOG_CATEGORY_EXTERN(LogLuprex, Display, All); // Messages that pertain to our Luprex integration with Unreal. -DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Warning, All); +DECLARE_LOG_CATEGORY_EXTERN(LogLuprexIntegration, Display, All); class LookAtDetector; @@ -100,6 +101,9 @@ public: // Execute a debugging command, typed on the GUI. void ExecuteDebuggingCommand(FlxLockedWrapper &w, const FString &fs); + // Get the Asset Lookup table. + const UlxAssetLookup *GetAssetLookup() const { return AssetLookup; } + // Transfer console output from the Luprex engine to unreal. void UpdateConsoleOutput(); @@ -124,8 +128,11 @@ public: virtual uint32 Run() override; // Get the current Luprex Game Mode Base, given a context object. - static ALuprexGameModeBase *FromContext(UObject *context); + static ALuprexGameModeBase *FromContext(const UObject *context); + // Asset Lookup by Name. + UPROPERTY() + UlxAssetLookup *AssetLookup; UPROPERTY() UlxTangibleManager *TangibleManager; diff --git a/Source/Integration/Tangible.cpp b/Source/Integration/Tangible.cpp index c92b08b5..d9bf0a06 100644 --- a/Source/Integration/Tangible.cpp +++ b/Source/Integration/Tangible.cpp @@ -33,10 +33,10 @@ void UlxTangible::SetActorBlueprint(const FString &XName) { } // Get the blueprint. - UClass *blueprint = Manager->GetTangibleClass(Name); + UClass *blueprint = UlxAssetLookup::GetTangibleClassByName(this, Name); if (blueprint == nullptr) { - blueprint = Manager->GetTangibleClass(DEFAULT_BLUEPRINT); + blueprint = UlxAssetLookup::GetTangibleClassByName(this, DEFAULT_BLUEPRINT); check(blueprint != nullptr); } @@ -121,7 +121,7 @@ void UlxTangible::MaybeExecuteAnimStateChanged() { if (blueprint.IsEmpty()) blueprint = DEFAULT_BLUEPRINT; SetActorBlueprint(blueprint); AActor *actor = GetActor(); - UFunction *aqchanged = UlxTangibleManager::GetAnimationQueueChanged(actor->GetClass()); + UFunction *aqchanged = actor->GetClass()->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); if (aqchanged != nullptr) { actor->ProcessEvent(aqchanged, nullptr); } diff --git a/Source/Integration/TangibleManager.cpp b/Source/Integration/TangibleManager.cpp index e6c3735c..774b4e7e 100644 --- a/Source/Integration/TangibleManager.cpp +++ b/Source/Integration/TangibleManager.cpp @@ -4,56 +4,15 @@ #include "TangibleManager.h" #include "Tangible.h" #include "LuprexGameModeBase.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetRegistry/ARFilter.h" -UFunction *UlxTangibleManager::GetAnimationQueueChanged(UClass *uclass) { - UFunction *result = uclass->FindFunctionByName(FName(TEXT("Animation Queue Changed"))); - if (result == nullptr) return nullptr; - if (result->ParmsSize != 0) return nullptr; - return result; -} - -UClass *UlxTangibleManager::GetTangibleClass(const FString &name) { - UPackage *Pack = LoadObject(nullptr, TEXT("/Game/Tangibles")); - UE_LOG(LogBlueprint, Verbose, TEXT("Pack=%ld"), int64(Pack)); - if (name.IsEmpty()) { - return nullptr; - } - if (name == TEXT("unknown")) { - return nullptr; - } - - FString path(TEXT("/Game/Tangibles/")); - path += TEXT("Tan"); - path += name; - path += TCHAR('.'); - path += TEXT("Tan"); - path += name; - path += TCHAR('_'); - path += TCHAR('C'); - UClass *result = LoadObject(nullptr, *path); - if (result == nullptr) { - UE_LOG(LogBlueprint, Error, TEXT("No such UClass: %s"), *path); - return nullptr; - } - if (!result->IsChildOf(AActor::StaticClass())) { - UE_LOG(LogBlueprint, Error, TEXT("UClass is not an actor: %s"), *path); - return nullptr; - } - UFunction *aqchanged = GetAnimationQueueChanged(result); - if (aqchanged == nullptr) { - UE_LOG(LogBlueprint, Error, TEXT("UClass does not have 'Animation Queue Changed' function: %s"), *path); - return nullptr; - } - return result; -} UlxTangibleManager::UlxTangibleManager() { - World = nullptr; PossessedTangible = nullptr; } -void UlxTangibleManager::Init(UWorld* world, ALuprexGameModeBase *gamemode) { - World = world; +void UlxTangibleManager::Init(ALuprexGameModeBase *gamemode) { GameMode = gamemode; } @@ -71,7 +30,7 @@ UlxTangible* UlxTangibleManager::MakeTangible(int64 id) { check(id > 0); UlxTangible*& t = IdToTangible.FindOrAdd(id); if (t == nullptr) { - t = NewObject(); + t = NewObject(this); t->Init(this, id); } return t; diff --git a/Source/Integration/TangibleManager.h b/Source/Integration/TangibleManager.h index 4e3d3a93..1d477801 100644 --- a/Source/Integration/TangibleManager.h +++ b/Source/Integration/TangibleManager.h @@ -21,10 +21,6 @@ public: using IdArray = CommonTypes::IdArray; using TanArray = TArray; - // A pointer to our world. - UPROPERTY() - TWeakObjectPtr World; - // A pointer to our game mode. UPROPERTY() TWeakObjectPtr GameMode; @@ -41,11 +37,7 @@ public: // Initialize the tangible manager. // - void Init(UWorld *world, ALuprexGameModeBase *gamemode); - - // Get a pointer to our world. - // - UWorld* GetWorld() const override { return World.Get(); } + void Init(ALuprexGameModeBase *gamemode); // Get a pointer to our game mode. ALuprexGameModeBase *GetGameMode() { return GameMode.Get(); } @@ -97,13 +89,5 @@ public: // Given an array of tangibles, return an array of tangible Ids. // static IdArray GetIds(const TanArray &tans); - - // Convert a blueprint name to a blueprint class. - // - UClass *GetTangibleClass(const FString &name); - - // Get the Animation Queue Changed function from a UClass. - // - static UFunction *GetAnimationQueueChanged(UClass *uclass); };