Skip to content

Commit

Permalink
Merge branch 'release/v0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
HarrisonHough committed Oct 15, 2024
2 parents 72f63f9 + be1dea7 commit 4436cd1
Show file tree
Hide file tree
Showing 140 changed files with 4,147 additions and 1,224 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [0.2.0] 2024-10-05

### Added

- Pagination support added to UI samples
- WebApis updates to fallback to cache if available

### Fixed

- WebApi's refactored to fix a number of bugs


## [0.1.0] 2024-09-05

### Added
Expand Down
Binary file removed Content/Samples/BasicLoader/BP_RpmActor.uasset
Binary file not shown.
Binary file removed Content/Samples/BasicLoader/BP_RpmActor_C.uasset
Binary file not shown.
Binary file removed Content/Samples/BasicLoader/BasicLoaderSample.umap
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed Content/Samples/BasicLoader/LoaderSample.umap
Binary file not shown.
Binary file added Content/Samples/BasicLoader/RpmBasicLoader.umap
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified Content/Samples/BasicUI/RpmBasicUISample.umap
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion RpmNextGen.uplugin
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "0.1",
"VersionName": "0.2",
"FriendlyName": "RpmNextGen",
"Description": "Ready Player Me Next Gen SDK",
"Category": "Other",
Expand Down
196 changes: 180 additions & 16 deletions Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp
Original file line number Diff line number Diff line change
@@ -1,52 +1,216 @@
#include "Api/Assets/AssetApi.h"
#include "Api/Assets/AssetApi.h"

#include "RpmNextGen.h"
#include "Settings/RpmDeveloperSettings.h"
#include "Api/Assets/Models/AssetListRequest.h"
#include "Api/Assets/Models/AssetListResponse.h"
#include "Api/Assets/Models/AssetTypeListRequest.h"
#include "Api/Auth/ApiKeyAuthStrategy.h"
#include "Cache/AssetCacheManager.h"
#include "Interfaces/IHttpResponse.h"

struct FCachedAssetData;
struct FAssetTypeListRequest;
const FString FAssetApi::BaseModelType = TEXT("baseModel");

FAssetApi::FAssetApi() : ApiRequestStrategy(EApiRequestStrategy::FallbackToCache)
{
Initialize();
}

FAssetApi::FAssetApi()
FAssetApi::FAssetApi(EApiRequestStrategy InApiRequestStrategy) : ApiRequestStrategy(InApiRequestStrategy)
{
OnApiResponse.BindRaw(this, &FAssetApi::HandleListAssetResponse);
Initialize();
}

FAssetApi::~FAssetApi()
{
}

void FAssetApi::Initialize()
{
if (bIsInitialized) return;

const URpmDeveloperSettings* Settings = GetDefault<URpmDeveloperSettings>();
OnRequestComplete.BindRaw(this, &FAssetApi::HandleAssetResponse);
if (Settings->ApplicationId.IsEmpty())
{
UE_LOG(LogReadyPlayerMe, Error, TEXT("Application ID is empty. Please set the Application ID in the Ready Player Me Developer Settings"));
}

if (!Settings->ApiKey.IsEmpty() || Settings->ApiProxyUrl.IsEmpty())
{
SetAuthenticationStrategy(MakeShared<FApiKeyAuthStrategy>());
}
bIsInitialized = true;
}

void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request)
{
if(ApiRequestStrategy == EApiRequestStrategy::CacheOnly)
{
LoadAssetsFromCache(Request.BuildQueryMap());
return;
}
const URpmDeveloperSettings* RpmSettings = GetDefault<URpmDeveloperSettings>();
ApiBaseUrl = RpmSettings->GetApiBaseUrl();
if(RpmSettings->ApplicationId.IsEmpty())
{
UE_LOG(LogTemp, Error, TEXT("Application ID is empty"));
UE_LOG(LogReadyPlayerMe, Error, TEXT("Application ID is empty"));
OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false);
return;
}
FString QueryString = Request.BuildQueryString();
const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *QueryString);
FApiRequest ApiRequest = FApiRequest();
ApiRequest.Url = Url;
ApiRequest.Method = GET;
const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets"), *ApiBaseUrl);
TSharedPtr<FApiRequest> ApiRequest = MakeShared<FApiRequest>();
ApiRequest->Url = Url;
ApiRequest->Method = GET;
ApiRequest->QueryParams = Request.BuildQueryMap();

DispatchRawWithAuth(ApiRequest);
}

void FAssetApi::HandleListAssetResponse(FString Response, bool bWasSuccessful)
void FAssetApi::ListAssetTypesAsync(const FAssetTypeListRequest& Request)
{
if(bWasSuccessful)
if(ApiRequestStrategy == EApiRequestStrategy::CacheOnly)
{
FAssetListResponse AssetListResponse = FAssetListResponse();
if(FJsonObjectConverter::JsonObjectStringToUStruct(Response, &AssetListResponse, 0, 0))
LoadAssetTypesFromCache();
return;
}
URpmDeveloperSettings* Settings = GetMutableDefault<URpmDeveloperSettings>();
ApiBaseUrl = Settings->GetApiBaseUrl();
if(Settings->ApplicationId.IsEmpty())
{
UE_LOG(LogReadyPlayerMe, Error, TEXT("Application ID is empty"));
OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false);
return;
}
FString QueryString = Request.BuildQueryString();
const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets/types%s"), *ApiBaseUrl, *QueryString);
TSharedPtr<FApiRequest> ApiRequest = MakeShared<FApiRequest>();
ApiRequest->Url = Url;
ApiRequest->Method = GET;

DispatchRawWithAuth( ApiRequest);
}

void FAssetApi::HandleAssetResponse(TSharedPtr<FApiRequest> ApiRequest, FHttpResponsePtr Response, bool bWasSuccessful)
{
const bool bIsTypeRequest = ApiRequest->Url.Contains(TEXT("/types"));
if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()))
{
FAssetTypeListResponse AssetTypeListResponse;

if (bIsTypeRequest && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &AssetTypeListResponse, 0, 0))
{
OnListAssetTypeResponse.ExecuteIfBound(AssetTypeListResponse, true);
return;
}
FAssetListResponse AssetListResponse;
if (!bIsTypeRequest && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &AssetListResponse, 0, 0))
{
OnListAssetsResponse.ExecuteIfBound(AssetListResponse, true);
return;
}
UE_LOG(LogTemp, Error, TEXT("Failed to parse API response"));
}

if(ApiRequestStrategy == EApiRequestStrategy::ApiOnly)
{
if(bIsTypeRequest)
{
UE_LOG(LogReadyPlayerMe, Error, TEXT("AssetApi:ListAssetTypesAsync request failed: %s"), *Response->GetContentAsString());
OnListAssetTypeResponse.ExecuteIfBound(FAssetTypeListResponse(), false);
return;
}
UE_LOG(LogReadyPlayerMe, Error, TEXT("AssetApi:ListAssetsAsync request failed: %s"), *Response->GetContentAsString());
OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false);
return;
}
if(bIsTypeRequest)
{
UE_LOG(LogReadyPlayerMe, Warning, TEXT("FAssetApi::ListAssetTypesAsync request failed, falling back to cache."));
LoadAssetTypesFromCache();
return;
}
UE_LOG(LogReadyPlayerMe, Warning, TEXT("FAssetApi::ListAssetsAsync request failed, falling back to cache."));
LoadAssetsFromCache(ApiRequest->QueryParams);
}

void FAssetApi::LoadAssetsFromCache(TMap<FString, FString> QueryParams)
{
if(QueryParams.Num() < 1)
{
OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false);
return;
}
const FString TypeKey = TEXT("type");
const FString ExcludeTypeKey = TEXT("excludeType");
FString Type = QueryParams.Contains(TypeKey) ? *QueryParams.Find(TypeKey) : FString();
FString ExcludeType = QueryParams.Contains(ExcludeTypeKey) ? *QueryParams.Find(ExcludeTypeKey) : FString();
TArray<FCachedAssetData> CachedAssets;

if(ExcludeType.IsEmpty())
{
CachedAssets = FAssetCacheManager::Get().GetAssetsOfType(Type);
}
else
{
UE_LOG(LogTemp, Error, TEXT("API Response was unsuccessful"));
auto ExtractQueryString = TEXT("excludeType=") + ExcludeType;
auto ExtractQueryArray = ExtractQueryValues(ExcludeType, ExcludeTypeKey);
CachedAssets = FAssetCacheManager::Get().GetAssetsExcludingTypes(ExtractQueryArray);
}

if (CachedAssets.Num() > 0)
{
FAssetListResponse AssetListResponse;
for (const FCachedAssetData& CachedAsset : CachedAssets)
{
FAsset Asset = CachedAsset.ToAsset();
AssetListResponse.Data.Add(Asset);
}
OnListAssetsResponse.ExecuteIfBound(AssetListResponse, true);
return;
}
UE_LOG(LogReadyPlayerMe, Warning, TEXT("No assets found in the cache."));
OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false);
}

void FAssetApi::HandleListAssetTypeResponse(FString Response, bool bWasSuccessful)
void FAssetApi::LoadAssetTypesFromCache()
{
TArray<FString> AssetTypes = FAssetCacheManager::Get().LoadAssetTypes();
if (AssetTypes.Num() > 0)
{
FAssetTypeListResponse AssetListResponse;
AssetListResponse.Data.Append(AssetTypes);
OnListAssetTypeResponse.ExecuteIfBound(AssetListResponse, true);
return;
}
UE_LOG(LogReadyPlayerMe, Warning, TEXT("No assets found in the cache."));
OnListAssetTypeResponse.ExecuteIfBound(FAssetTypeListResponse(), false);
}

TArray<FString> FAssetApi::ExtractQueryValues(const FString& QueryString, const FString& Key)
{
TArray<FString> Values;
TArray<FString> Pairs;

// Split the query string by '&' to separate each key-value pair
QueryString.ParseIntoArray(Pairs, TEXT("&"), true);

// Iterate through all pairs and check if they match the key
for (const FString& Pair : Pairs)
{
FString KeyPart, ValuePart;

// Split the pair by '=' to get the key and value
if (Pair.Split(TEXT("="), &KeyPart, &ValuePart))
{
// If the key matches the one we're looking for, add the value to the array
if (KeyPart.Equals(Key, ESearchCase::IgnoreCase))
{
Values.Add(ValuePart);
}
}
}

return Values;
}
61 changes: 61 additions & 0 deletions Source/RpmNextGen/Private/Api/Assets/AssetGlbLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "Api/Assets/AssetGlbLoader.h"
#include "Api/Assets/AssetLoaderContext.h"
#include "HttpModule.h"
#include "Cache/AssetCacheManager.h"
#include "Interfaces/IHttpResponse.h"

FAssetGlbLoader::FAssetGlbLoader()
{
Http = &FHttpModule::Get();
}

FAssetGlbLoader::~FAssetGlbLoader()
{
}

void FAssetGlbLoader::LoadGlb(const FAsset& Asset, const FString& BaseModelId, bool bStoreInCache)
{
FCachedAssetData StoredAsset;
if (FAssetCacheManager::Get().GetCachedAsset(Asset.Id, StoredAsset))
{
TArray<uint8> GlbData;
const FString StoredGlbPath = StoredAsset.GetGlbPathForBaseModelId(BaseModelId);
if(FFileHelper::LoadFileToArray(GlbData, *StoredGlbPath))
{
OnGlbLoaded.ExecuteIfBound(Asset, GlbData);
return;
}
}
const TSharedRef<FAssetLoadingContext> Context = MakeShared<FAssetLoadingContext>(Asset, BaseModelId, bStoreInCache);
LoadGlb(Context);
}

void FAssetGlbLoader::LoadGlb(TSharedRef<FAssetLoadingContext> Context)
{
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
Request->SetURL(Context->Asset.GlbUrl);
Request->SetVerb(TEXT("GET"));

TSharedPtr<FAssetGlbLoader> ThisPtr = SharedThis(this);
Request->OnProcessRequestComplete().BindLambda([ThisPtr, Context](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
ThisPtr->GlbLoaded(Response, bWasSuccessful, Context);
});
Request->ProcessRequest();
}

void FAssetGlbLoader::GlbLoaded(TSharedPtr<IHttpResponse> Response, const bool bWasSuccessful, const TSharedRef<FAssetLoadingContext>& Context)
{
if (bWasSuccessful && Response.IsValid())
{
Context->Data = Response->GetContent();
if (Context->bStoreInCache)
{
FAssetCacheManager::Get().StoreAndTrackGlb(*Context);
}
OnGlbLoaded.ExecuteIfBound(Context->Asset, Context->Data);
return;
}
UE_LOG(LogReadyPlayerMe, Error, TEXT("Failed to load GLB from URL: %s"), *Context->Asset.GlbUrl);
OnGlbLoaded.ExecuteIfBound(Context->Asset, TArray<uint8>());
}
60 changes: 60 additions & 0 deletions Source/RpmNextGen/Private/Api/Assets/AssetIconLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "Api/Assets/AssetIconLoader.h"
#include "Api/Assets/AssetLoaderContext.h"
#include "HttpModule.h"
#include "Cache/AssetCacheManager.h"
#include "Interfaces/IHttpResponse.h"

FAssetIconLoader::FAssetIconLoader()
{
Http = &FHttpModule::Get();
}

FAssetIconLoader::~FAssetIconLoader()
{
}

void FAssetIconLoader::LoadIcon(const FAsset& Asset, bool bStoreInCache)
{
FCachedAssetData StoredAsset;
if (FAssetCacheManager::Get().GetCachedAsset(Asset.Id, StoredAsset))
{
TArray<uint8> IconData;
if(FFileHelper::LoadFileToArray(IconData, * FFileUtility::GetFullPersistentPath(StoredAsset.RelativeIconFilePath)))
{
OnIconLoaded.ExecuteIfBound(Asset, IconData);
return;
}
}
const TSharedRef<FAssetLoadingContext> Context = MakeShared<FAssetLoadingContext>(Asset, "", bStoreInCache);
LoadIcon(Context);
}

void FAssetIconLoader::LoadIcon(TSharedRef<FAssetLoadingContext> Context)
{
TSharedRef<IHttpRequest> Request = Http->CreateRequest();
Request->SetURL(Context->Asset.IconUrl);
Request->SetVerb(TEXT("GET"));

TSharedPtr<FAssetIconLoader> ThisPtr = SharedThis(this);
Request->OnProcessRequestComplete().BindLambda([ThisPtr, Context](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
ThisPtr->IconLoaded(Response, bWasSuccessful, Context);
});
Request->ProcessRequest();
}

void FAssetIconLoader::IconLoaded(TSharedPtr<IHttpResponse> Response, bool bWasSuccessful, const TSharedRef<FAssetLoadingContext>& Context)
{
if (bWasSuccessful && Response.IsValid())
{
Context->Data = Response->GetContent();
if (Context->bStoreInCache)
{
FAssetCacheManager::Get().StoreAndTrackIcon(*Context);
}
OnIconLoaded.ExecuteIfBound(Context->Asset, Context->Data);
return;
}
UE_LOG(LogReadyPlayerMe, Error, TEXT("Failed to load icon from URL: %s"), *Context->Asset.IconUrl);
OnIconLoaded.ExecuteIfBound(Context->Asset, TArray<uint8>());
}
Loading

0 comments on commit 4436cd1

Please sign in to comment.