Add AssetLookup module to find assets by name.

This commit is contained in:
2025-04-01 22:31:27 -04:00
parent b26d56048f
commit aa511b9b8c
11 changed files with 250 additions and 78 deletions

Binary file not shown.

View File

@@ -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<FAssetData> 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<FAssetData> 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<UStaticMesh>(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<UClass>(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;
}

View File

@@ -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<FName, FString> CachedTangibles;
// Map from asset name to to full loadable path.
UPROPERTY()
TMap<FName, FString> 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);
};

View File

@@ -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; }
};

View File

@@ -18,7 +18,8 @@ DEFINE_LOG_CATEGORY(LogLuprexIntegration);
ALuprexGameModeBase::ALuprexGameModeBase()
{
TangibleManager = NewObject<UlxTangibleManager>();
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<UlxAssetLookup>(this);
AssetLookup->RebuildIndex();
// Initialize the tangible manager.
TangibleManager = NewObject<UlxTangibleManager>();
TangibleManager->Init(GetWorld(), this);
TangibleManager = NewObject<UlxTangibleManager>(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<ALuprexGameModeBase>();
if (result == nullptr) {
UE_LOG(LogBlueprint, Fatal, TEXT("Not currently using a Luprex Game Mode."));

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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<UPackage>(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<UClass>(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<UlxTangible>();
t = NewObject<UlxTangible>(this);
t->Init(this, id);
}
return t;

View File

@@ -21,10 +21,6 @@ public:
using IdArray = CommonTypes::IdArray;
using TanArray = TArray<UlxTangible*>;
// A pointer to our world.
UPROPERTY()
TWeakObjectPtr<UWorld> World;
// A pointer to our game mode.
UPROPERTY()
TWeakObjectPtr<ALuprexGameModeBase> 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);
};