Ejemplo n.º 1
0
void UAssetObjectProperty::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const
{
	FAssetPtr& AssetPtr = *(FAssetPtr*)PropertyValue;

	FStringAssetReference ID;
	UObject *Object = AssetPtr.Get();

	if (Object)
	{
		// Use object in case name has changed.
		ID = FStringAssetReference(Object);
	}
	else
	{
		ID = AssetPtr.GetUniqueID();
	}

	if (0 != (PortFlags & PPF_ExportCpp))
	{
		ValueStr += FString::Printf(TEXT("FStringAssetReference(TEXT(\"%s\"))"), *ID.ToString().ReplaceCharWithEscapedChar());
		return;
	}

	if (!ID.ToString().IsEmpty())
	{
		ValueStr += ID.ToString();
	}
	else
	{
		ValueStr += TEXT("None");
	}
}
Ejemplo n.º 2
0
void FStreamableManager::AsyncLoadCallback(FStringAssetReference Request)
{
	FStringAssetReference TargetName = Request;
	FStreamable* Existing = StreamableItems.FindRef(TargetName);

	UE_LOG(LogStreamableManager, Verbose, TEXT("Stream Complete callback %s"), *TargetName.ToString());
	if (!Existing)
	{
		// hmm, maybe it was redirected by a consolidate
		TargetName = ResolveRedirects(TargetName);
		Existing = StreamableItems.FindRef(TargetName);
	}
	if (Existing && Existing->bAsyncLoadRequestOutstanding)
	{
		Existing->bAsyncLoadRequestOutstanding = false;
		if (!Existing->Target)
		{
			FindInMemory(TargetName, Existing);
		}

		CheckCompletedRequests(Request, Existing);
	}
	if (Existing->Target)
	{
		UE_LOG(LogStreamableManager, Verbose, TEXT("    Found target %s"), *Existing->Target->GetFullName());
	}
	else
	{
		// Async load failed to find the object
		Existing->bLoadFailed = true;
		UE_LOG(LogStreamableManager, Verbose, TEXT("    Failed async load."), *TargetName.ToString());
	}
}
void UAssetObjectProperty::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const
{
    FAssetPtr& AssetPtr = *(FAssetPtr*)PropertyValue;

    FStringAssetReference ID;
    UObject *Object = AssetPtr.Get();

    if (Object)
    {
        // Use object in case name has changed.
        ID = FStringAssetReference(Object);
    }
    else
    {
        ID = AssetPtr.GetUniqueID();
    }

    if (!ID.ToString().IsEmpty())
    {
        ValueStr += ID.ToString();
    }
    else
    {
        ValueStr += TEXT("None");
    }
}
Ejemplo n.º 4
0
struct FStreamable* FStreamableManager::StreamInternal(FStringAssetReference const& InTargetName)
{
	check(IsInGameThread());
	UE_LOG(LogStreamableManager, Verbose, TEXT("Asynchronous load %s"), *InTargetName.ToString());

	if (FPackageName::IsShortPackageName(InTargetName.ToString()))
	{
		UE_LOG(LogStreamableManager, Error, TEXT("     Can't load invalid package name %s"), *InTargetName.ToString());
		return NULL;
	}

	FStringAssetReference TargetName = ResolveRedirects(InTargetName);
	FStreamable* Existing = StreamableItems.FindRef(TargetName);
	if (Existing)
	{
		if (Existing->bAsyncUnloadRequestOutstanding)
		{
			// It's valid to have a live pointer if an async loaded object was hard referenced later
			check(!Existing->bAsyncLoadRequestOutstanding); // we should not be both loading and unloading
			UE_LOG(LogStreamableManager, Verbose, TEXT("     Aborted unload for %s"), *TargetName.ToString());
			Existing->bAsyncUnloadRequestOutstanding = false;
			check(Existing->Target || Existing->bLoadFailed); // should not be an unload request unless the target is valid
			return Existing;
		}
		if (Existing->bAsyncLoadRequestOutstanding)
		{
			UE_LOG(LogStreamableManager, Verbose, TEXT("     Already in progress %s"), *TargetName.ToString());
			check(!Existing->bAsyncUnloadRequestOutstanding); // we should not be both loading and unloading
			check(!Existing->Target); // should not be an load request unless the target is invalid
			return Existing; // already have one in process
		}
		if (Existing->Target)
		{
			UE_LOG(LogStreamableManager, Verbose, TEXT("     Already Loaded %s"), *TargetName.ToString());
			return Existing; 
		}
	}
	else
	{
		Existing = StreamableItems.Add(TargetName, new FStreamable());
	}

	FindInMemory(TargetName, Existing);

	if (!Existing->Target)
	{
		FString Package = TargetName.ToString();
		int32 FirstDot = Package.Find(TEXT("."));
		if (FirstDot != INDEX_NONE)
		{
			Package = Package.Left(FirstDot);
		}

		Existing->bAsyncLoadRequestOutstanding = true;
		LoadPackageAsync(Package,
			FLoadPackageAsyncDelegate::CreateStatic(&AsyncLoadCallbackWrapper, new FCallback(TargetName, this))
			);
	}
	return Existing;
}
/** Handles cleaning up an object library if it matches the passed in object */
void UGameplayCueManager::HandleAssetDeleted(UObject *Object)
{
	FStringAssetReference StringRefToRemove;
	UBlueprint* Blueprint = Cast<UBlueprint>(Object);
	if (Blueprint && Blueprint->GeneratedClass)
	{
		UGameplayCueNotify_Static* StaticCDO = Cast<UGameplayCueNotify_Static>(Blueprint->GeneratedClass->ClassDefaultObject);
		AGameplayCueNotify_Actor* ActorCDO = Cast<AGameplayCueNotify_Actor>(Blueprint->GeneratedClass->ClassDefaultObject);
		
		if (StaticCDO || ActorCDO)
		{
			StringRefToRemove.SetPath(Blueprint->GeneratedClass->GetPathName());
		}
	}

	if (StringRefToRemove.IsValid())
	{
		TArray<FStringAssetReference> StringRefs;
		StringRefs.Add(StringRefToRemove);
		check(GlobalCueSet);
		GlobalCueSet->RemoveCuesByStringRefs(StringRefs);

		OnGameplayCueNotifyAddOrRemove.Broadcast();
	}
}
Ejemplo n.º 6
0
bool FStreamableManager::IsAsyncLoadComplete(FStringAssetReference const& InTargetName)
{
	check(IsInGameThread());
	FStringAssetReference TargetName = ResolveRedirects(InTargetName);
	FStreamable* Existing = StreamableItems.FindRef(TargetName);
	UE_LOG(LogStreamableManager, Verbose, TEXT("IsStreamComplete %s  -> %d"), *TargetName.ToString(), !Existing || !Existing->bAsyncLoadRequestOutstanding);
	return !Existing || !Existing->bAsyncLoadRequestOutstanding;
}
Ejemplo n.º 7
0
FArchive& FObjectReader::operator<<( class FAssetPtr& AssetPtr )
{
	FArchive& Ar = *this;
	FStringAssetReference ID;
	ID.Serialize(Ar);

	AssetPtr = ID;
	return Ar;
}
Ejemplo n.º 8
0
FArchive& FDuplicateDataReader::operator<<( FAssetPtr& AssetPtr)
{
	FArchive& Ar = *this;
	FStringAssetReference ID;
	ID.Serialize(Ar);

	AssetPtr = ID;
	return Ar;
}
Ejemplo n.º 9
0
void USoundBase::PostInitProperties()
{
	Super::PostInitProperties();

	const FStringAssetReference DefaultSoundClassName = GetDefault<UAudioSettings>()->DefaultSoundClassName;
	if (DefaultSoundClassName.IsValid())
	{
		SoundClassObject = LoadObject<USoundClass>(NULL, *DefaultSoundClassName.ToString());
	}
}
// Backwards compat for map strings
void FixMapAssetRef(FStringAssetReference& MapAssetReference)
{
	const FString AssetRefStr = MapAssetReference.ToString();
	int32 DummyIndex;
	if (!AssetRefStr.IsEmpty() && !AssetRefStr.FindLastChar(TEXT('.'), DummyIndex))
	{
		FString MapName, MapPath;
		AssetRefStr.Split(TEXT("/"), &MapPath, &MapName, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
		MapAssetReference.SetPath(FString::Printf(TEXT("%s/%s.%s"),*MapPath, *MapName, *MapName));
	}
};
Ejemplo n.º 11
0
UObject* FStreamableManager::GetStreamed(FStringAssetReference const& InTargetName)
{
	check(IsInGameThread());
	FStringAssetReference TargetName = ResolveRedirects(InTargetName);
	FStreamable* Existing = StreamableItems.FindRef(TargetName);
	if (Existing && Existing->Target)
	{
		UE_LOG(LogStreamableManager, Verbose, TEXT("GetStreamed %s  -> %s"), *TargetName.ToString(), *Existing->Target->GetFullName());
		return Existing->Target;
	}
	UE_LOG(LogStreamableManager, Verbose, TEXT("GetStreamed %s  -> NULL"), *TargetName.ToString());
	return NULL;
}
Ejemplo n.º 12
0
void USoundBase::PostInitProperties()
{
	Super::PostInitProperties();

	if (USoundBase::DefaultSoundClassObject == nullptr)
	{
		const FStringAssetReference DefaultSoundClassName = GetDefault<UAudioSettings>()->DefaultSoundClassName;
		if (DefaultSoundClassName.IsValid())
		{
			USoundBase::DefaultSoundClassObject = LoadObject<USoundClass>(nullptr, *DefaultSoundClassName.ToString());
		}
	}
	SoundClassObject = USoundBase::DefaultSoundClassObject;

	if (USoundBase::DefaultSoundSubmixObject == nullptr)
	{
		const FStringAssetReference DefaultSoundSubmixName = GetDefault<UAudioSettings>()->DefaultSoundSubmixName;
		if (DefaultSoundSubmixName.IsValid())
		{
			USoundBase::DefaultSoundSubmixObject = LoadObject<USoundSubmix>(nullptr, *DefaultSoundSubmixName.ToString());
		}
	}
	SoundSubmixObject = USoundBase::DefaultSoundSubmixObject;

	if (USoundBase::DefaultSoundConcurrencyObject == nullptr)
	{
		const FStringAssetReference DefaultSoundConcurrencyName = GetDefault<UAudioSettings>()->DefaultSoundConcurrencyName;
		if (DefaultSoundConcurrencyName.IsValid())
		{
			USoundBase::DefaultSoundConcurrencyObject = LoadObject<USoundConcurrency>(nullptr, *DefaultSoundConcurrencyName.ToString());
		}
	}
	SoundConcurrencySettings = USoundBase::DefaultSoundConcurrencyObject;

}
Ejemplo n.º 13
0
void FStreamableManager::Unload(FStringAssetReference const& InTargetName)
{
	check(IsInGameThread());
	FStringAssetReference TargetName = ResolveRedirects(InTargetName);
	FStreamable* Existing = StreamableItems.FindRef(TargetName);
	if (Existing)
	{
		UE_LOG(LogStreamableManager, Verbose, TEXT("Unload %s"), *TargetName.ToString());
		Existing->bAsyncLoadRequestOutstanding = false;
		Existing->bAsyncUnloadRequestOutstanding = true;
	}
	else
	{
		UE_LOG(LogStreamableManager, Verbose, TEXT("Attempt to unload %s, but it isn't loaded"), *TargetName.ToString());
	}
}
Ejemplo n.º 14
0
FArchive& FLinkerSave::operator<<( FAssetPtr& AssetPtr)
{
	FStringAssetReference ID;
	UObject *Object = AssetPtr.Get();

	if (Object)
	{
		// Use object in case name has changed. 
		ID = FStringAssetReference(Object);
	}
	else
	{
		ID = AssetPtr.GetUniqueID();
	}

	ID.Serialize(*this);
	return *this;
}
Ejemplo n.º 15
0
FStringAssetReference FStreamableManager::ResolveRedirects(FStringAssetReference const& Target) const
{
	FStringAssetReference const* Redir = StreamableRedirects.Find(Target);
	if (Redir)
	{
		check(Target != *Redir);
		UE_LOG(LogStreamableManager, Verbose, TEXT("Redirected %s -> %s"), *Target.ToString(), *Redir->ToString());
		return *Redir;
	}
	return Target;
}
void FPaperJsonSpriteSheetImporter::SetReimportData(const TArray<FString>& ExistingSpriteNames, const TArray< TAssetPtr<class UPaperSprite> >& ExistingSpriteAssetPtrs)
{
	check(ExistingSpriteNames.Num() == ExistingSpriteAssetPtrs.Num());
	if (ExistingSpriteNames.Num() == ExistingSpriteAssetPtrs.Num())
	{
		for (int i = 0; i < ExistingSpriteAssetPtrs.Num(); ++i)
		{
			const TAssetPtr<class UPaperSprite> SpriteAssetPtr = ExistingSpriteAssetPtrs[i];
			FStringAssetReference SpriteStringRef = SpriteAssetPtr.ToStringReference();
			if (!SpriteStringRef.ToString().IsEmpty())
			{
				UPaperSprite* LoadedSprite = Cast<UPaperSprite>(StaticLoadObject(UPaperSprite::StaticClass(), nullptr, *SpriteStringRef.ToString(), nullptr, LOAD_None, nullptr));
				if (LoadedSprite != nullptr)
				{
					ExistingSprites.Add(ExistingSpriteNames[i], LoadedSprite);
				}
			}
		}
	}
	bIsReimporting = true;
}
void UGameplayCueManager::HandleAssetAdded(UObject *Object)
{
	UBlueprint* Blueprint = Cast<UBlueprint>(Object);
	if (Blueprint && Blueprint->GeneratedClass)
	{
		UGameplayCueNotify_Static* StaticCDO = Cast<UGameplayCueNotify_Static>(Blueprint->GeneratedClass->ClassDefaultObject);
		AGameplayCueNotify_Actor* ActorCDO = Cast<AGameplayCueNotify_Actor>(Blueprint->GeneratedClass->ClassDefaultObject);
		
		if (StaticCDO || ActorCDO)
		{
			if (IsAssetInLoadedPaths(Object))
			{
				FStringAssetReference StringRef;
				StringRef.SetPath(Blueprint->GeneratedClass->GetPathName());

				TArray<FGameplayCueReferencePair> CuesToAdd;
				if (StaticCDO)
				{
					CuesToAdd.Add(FGameplayCueReferencePair(StaticCDO->GameplayCueTag, StringRef));
				}
				else if (ActorCDO)
				{
					CuesToAdd.Add(FGameplayCueReferencePair(ActorCDO->GameplayCueTag, StringRef));
				}

				check(GlobalCueSet);
				GlobalCueSet->AddCues(CuesToAdd);

				OnGameplayCueNotifyAddOrRemove.Broadcast();

			
			}
			else
			{
				VerifyNotifyAssetIsInValidPath(Blueprint->GetOuter()->GetPathName());
			}
		}
	}
}
Ejemplo n.º 18
0
USkeletalMesh* UAnimationAsset::GetPreviewMesh() 
{
	USkeletalMesh* PreviewMesh = PreviewSkeletalMesh.Get();
	if(!PreviewMesh)
	{
		// if preview mesh isn't loaded, see if we have set
		FStringAssetReference PreviewMeshStringRef = PreviewSkeletalMesh.ToStringReference();
		// load it since now is the time to load
		if(!PreviewMeshStringRef.ToString().IsEmpty())
		{
			PreviewMesh = Cast<USkeletalMesh>(StaticLoadObject(USkeletalMesh::StaticClass(), NULL, *PreviewMeshStringRef.ToString(), NULL, LOAD_None, NULL));
			// if somehow skeleton changes, just nullify it. 
			if (PreviewMesh && PreviewMesh->Skeleton != Skeleton)
			{
				PreviewMesh = NULL;
				SetPreviewMesh(NULL);
			}
		}
	}

	return PreviewMesh;
}
Ejemplo n.º 19
0
void FStreamableManager::FindInMemory(FStringAssetReference& InOutTargetName, struct FStreamable* Existing)
{
	check(Existing);
	check(!Existing->bAsyncUnloadRequestOutstanding);
	check(!Existing->bAsyncLoadRequestOutstanding);
	UE_LOG(LogStreamableManager, Verbose, TEXT("     Searching in memory for %s"), *InOutTargetName.ToString());
	Existing->Target = StaticFindObject(UObject::StaticClass(), NULL, *InOutTargetName.ToString());

	UObjectRedirector* Redir = Cast<UObjectRedirector>(Existing->Target);
	if (Redir)
	{
		Existing->Target = Redir->DestinationObject;
		UE_LOG(LogStreamableManager, Verbose, TEXT("     Found redirect %s"), *Redir->GetFullName());
		if (!Existing->Target)
		{
			Existing->bLoadFailed = true;
			UE_LOG(LogStreamableManager, Warning, TEXT("Destination of redirector was not found %s -> %s."), *InOutTargetName.ToString(), *Redir->GetFullName());
		}
		else
		{
			UE_LOG(LogStreamableManager, Verbose, TEXT("     Redirect to %s"), *Redir->DestinationObject->GetFullName());
		}
	}
	if (Existing->Target)
	{
		FStringAssetReference PossiblyNewName(Existing->Target->GetPathName());
		if (InOutTargetName != PossiblyNewName)
		{
			UE_LOG(LogStreamableManager, Verbose, TEXT("     Name changed to %s"), *PossiblyNewName.ToString());
			StreamableRedirects.Add(InOutTargetName, PossiblyNewName);
			StreamableItems.Add(PossiblyNewName, Existing);
			StreamableItems.Remove(InOutTargetName);
			InOutTargetName = PossiblyNewName; // we are done with the old name
		}
		UE_LOG(LogStreamableManager, Verbose, TEXT("     Found in memory %s"), *Existing->Target->GetFullName());
		Existing->bLoadFailed = false;
	}
}
Ejemplo n.º 20
0
USkeletalMesh* USkeleton::GetPreviewMesh(bool bFindIfNotSet)
{
	USkeletalMesh* PreviewMesh = PreviewSkeletalMesh.Get();
	if(!PreviewMesh)
	{
		// if preview mesh isn't loaded, see if we have set
		FStringAssetReference PreviewMeshStringRef = PreviewSkeletalMesh.ToStringReference();
		// load it since now is the time to load
		if (!PreviewMeshStringRef.ToString().IsEmpty())
		{
			PreviewMesh = Cast<USkeletalMesh>(StaticLoadObject(USkeletalMesh::StaticClass(), NULL, *PreviewMeshStringRef.ToString(), NULL, LOAD_None, NULL));
		}
		
		// if not existing, and if bFindIfNotExisting is true, then try find one
		if (!PreviewMesh && bFindIfNotSet)
		{
			FARFilter Filter;
			Filter.ClassNames.Add(USkeletalMesh::StaticClass()->GetFName());

			FString SkeletonString = FAssetData(this).GetExportTextName();
			Filter.TagsAndValues.Add(GET_MEMBER_NAME_CHECKED(USkeletalMesh, Skeleton), SkeletonString);

			TArray<FAssetData> AssetList;
			FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
			AssetRegistryModule.Get().GetAssets(Filter, AssetList);

			if(AssetList.Num() > 0)
			{
				SetPreviewMesh( Cast<USkeletalMesh>(AssetList[0].GetAsset()), false );
				// update PreviewMesh
				PreviewMesh = PreviewSkeletalMesh.Get();
			}			
		}
	}

	return PreviewMesh;
}
UAudioComponent* CreateVoiceAudioComponent(uint32 SampleRate)
{
	UAudioComponent* AudioComponent = nullptr;
	if (GEngine != nullptr)
	{
		if (FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice())
		{
			USoundWaveProcedural* SoundStreaming = NewObject<USoundWaveProcedural>();
			SoundStreaming->SampleRate = SampleRate;
			SoundStreaming->NumChannels = 1;
			SoundStreaming->Duration = INDEFINITELY_LOOPING_DURATION;
			SoundStreaming->SoundGroup = SOUNDGROUP_Voice;
			SoundStreaming->bLooping = false;

			AudioComponent = AudioDevice->CreateComponent(SoundStreaming, nullptr, nullptr, false);
			if (AudioComponent)
			{
				AudioComponent->bIsUISound = true;
				AudioComponent->bAllowSpatialization = false;
				AudioComponent->SetVolumeMultiplier(1.5f);

				const FStringAssetReference VoiPSoundClassName = GetDefault<UAudioSettings>()->VoiPSoundClass;
				if (VoiPSoundClassName.IsValid())
				{
					AudioComponent->SoundClassOverride = LoadObject<USoundClass>(nullptr, *VoiPSoundClassName.ToString());
				}
			}
			else
			{
				UE_LOG(LogVoiceDecode, Warning, TEXT("Unable to create voice audio component!"));
			}
		}
	}

	return AudioComponent;
}
bool FStringAssetReference::operator==(FStringAssetReference const& Other) const
{
	return ToString() == Other.ToString();
}
void FStringAssetReference::operator=(FStringAssetReference const& Other)
{
	SetPath(Other.ToString());
}
Ejemplo n.º 24
0
FArchive& FObjectWriter::operator<<( class FAssetPtr& AssetPtr )
{
	FStringAssetReference ID = AssetPtr.GetUniqueID();
	ID.Serialize(*this);
	return *this;
}
void UGameplayCueManager::BuildCuesToAddToGlobalSet(const TArray<FAssetData>& AssetDataList, FName TagPropertyName, bool bAsyncLoadAfterAdd, TArray<FGameplayCueReferencePair>& OutCuesToAdd, FOnGameplayCueNotifySetLoaded OnLoaded, FShouldLoadGCNotifyDelegate ShouldLoad)
{
	IGameplayTagsModule& GameplayTagsModule = IGameplayTagsModule::Get();

	TArray<FStringAssetReference> AssetsToLoad;
	AssetsToLoad.Reserve(AssetDataList.Num());

	for (FAssetData Data: AssetDataList)
	{
		// If ShouldLoad delegate is bound and it returns false, don't load this one
		if (ShouldLoad.IsBound() && (ShouldLoad.Execute(Data) == false))
		{
			continue;
		}

		const FString* FoundGameplayTag = Data.TagsAndValues.Find(TagPropertyName);
		if (FoundGameplayTag && FoundGameplayTag->Equals(TEXT("None")) == false)
		{
			const FString* GeneratedClassTag = Data.TagsAndValues.Find(TEXT("GeneratedClass"));
			if (GeneratedClassTag == nullptr)
			{
				ABILITY_LOG(Warning, TEXT("Unable to find GeneratedClass value for AssetData %s"), *Data.ObjectPath.ToString());
				continue;
			}

			ABILITY_LOG(Log, TEXT("GameplayCueManager Found: %s / %s"), **FoundGameplayTag, **GeneratedClassTag);

			FGameplayTag  GameplayCueTag = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTag(FName(**FoundGameplayTag), false);
			if (GameplayCueTag.IsValid())
			{
				// Add a new NotifyData entry to our flat list for this one
				FStringAssetReference StringRef;
				StringRef.SetPath(FPackageName::ExportTextPathToObjectPath(*GeneratedClassTag));

				OutCuesToAdd.Add(FGameplayCueReferencePair(GameplayCueTag, StringRef));

				AssetsToLoad.Add(StringRef);
			}
			else
			{
				ABILITY_LOG(Warning, TEXT("Found GameplayCue tag %s in asset %s but there is no corresponding tag in the GameplayTagManager."), **FoundGameplayTag, *Data.PackageName.ToString());
			}
		}
	}

	if (bAsyncLoadAfterAdd)
	{
		auto ForwardLambda = [](TArray<FStringAssetReference> AssetList, FOnGameplayCueNotifySetLoaded OnLoadedDelegate)
		{
			OnLoadedDelegate.ExecuteIfBound(AssetList);
		};

		if (AssetsToLoad.Num() > 0)
		{			
			StreamableManager.RequestAsyncLoad(AssetsToLoad, FStreamableDelegate::CreateStatic( ForwardLambda, AssetsToLoad, OnLoaded));
		}
		else
		{
			// Still fire the delegate even if nothing was found to load
			OnLoaded.ExecuteIfBound(AssetsToLoad);
		}
	}
}
Ejemplo n.º 26
0
UObject* FStreamableManager::SynchronousLoad(FStringAssetReference const& InTargetName)
{
	UE_LOG(LogStreamableManager, Verbose, TEXT("Synchronous load %s"), *InTargetName.ToString());

	if (FPackageName::IsShortPackageName(InTargetName.ToString()))
	{
		UE_LOG(LogStreamableManager, Error, TEXT("     Can't load invalid package name %s"), *InTargetName.ToString());
		return NULL;
	}

	FStringAssetReference TargetName = ResolveRedirects(InTargetName);
	FStreamable* Existing = StreamableItems.FindRef(TargetName);
	while (Existing && Existing->bAsyncLoadRequestOutstanding)
	{
		UE_LOG(LogStreamableManager, Verbose, TEXT("     Flushing async load for %s"), *TargetName.ToString());
		check(!Existing->bAsyncUnloadRequestOutstanding); // we should not be both loading and unloading
		FlushAsyncLoading(); 
		// the async request might have found a redirect and retried
		TargetName = ResolveRedirects(TargetName);
		Existing = StreamableItems.FindRef(TargetName);
	}
	if (!Existing)
	{
		Existing = StreamableItems.Add(TargetName, new FStreamable());
	}
	check(!Existing->bAsyncLoadRequestOutstanding); // should have already dealt with this

	if (Existing->bAsyncUnloadRequestOutstanding)
	{
		check(!Existing->bAsyncLoadRequestOutstanding); // we should not be both loading and unloading
		UE_LOG(LogStreamableManager, Verbose, TEXT("     Aborted unload for %s"), *TargetName.ToString());
		Existing->bAsyncUnloadRequestOutstanding = false;
	}
	check(!Existing->bAsyncUnloadRequestOutstanding); // should have already dealt with this
	check(!Existing->WeakTarget.Get()); // weak target is only valid during GC
	if (!Existing->Target)
	{
		UE_LOG(LogStreamableManager, Verbose, TEXT("     Static loading %s"), *TargetName.ToString());
		Existing->Target = StaticLoadObject(UObject::StaticClass(), NULL, *TargetName.ToString());
		// need to manually detect redirectors because the above call only expects to load a UObject::StaticClass() type
		while (UObjectRedirector* Redirector = Cast<UObjectRedirector>(Existing->Target))
		{
				Existing->Target = Redirector->DestinationObject;
		}
		if (Existing->Target)
		{
			UE_LOG(LogStreamableManager, Verbose, TEXT("     Static loaded %s"), *Existing->Target->GetFullName());
			FStringAssetReference PossiblyNewName(Existing->Target->GetPathName());
			if (PossiblyNewName != TargetName)
			{
				UE_LOG(LogStreamableManager, Verbose, TEXT("     Which redirected to %s"), *PossiblyNewName.ToString());
				StreamableRedirects.Add(TargetName, PossiblyNewName);
				StreamableItems.Add(PossiblyNewName, Existing);
				StreamableItems.Remove(TargetName);
				TargetName = PossiblyNewName; // we are done with the old name
			}
		}
		else
		{
			Existing->bLoadFailed = true;
			UE_LOG(LogStreamableManager, Log, TEXT("Failed attempt to load %s"), *TargetName.ToString());
		}
	}
	else
	{
		Existing->bLoadFailed = false;
	}
	return Existing->Target;
}
FArchive& FDuplicateDataWriter::operator<<(FAssetPtr& AssetPtr)
{
	FStringAssetReference ID = AssetPtr.GetUniqueID();
	ID.Serialize(*this);
	return *this;
}
void FPaperSpriteSheetAssetTypeActions::ExecuteCreateFlipbooks(TArray<TWeakObjectPtr<UPaperSpriteSheet>> Objects)
{	
	for (int SpriteSheetIndex = 0; SpriteSheetIndex < Objects.Num(); ++SpriteSheetIndex)
	{
		UPaperSpriteSheet* SpriteSheet = Objects[SpriteSheetIndex].Get();
		if (SpriteSheet != nullptr)
		{
			const FString PackagePath = FPackageName::GetLongPackagePath(SpriteSheet->GetOutermost()->GetPathName());

			FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
			FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
			
			check(SpriteSheet->SpriteNames.Num() == SpriteSheet->Sprites.Num());
			bool useSpriteNames = (SpriteSheet->SpriteNames.Num() == SpriteSheet->Sprites.Num());

			// Create a list of sprites and sprite names to feed into paper flipbook helpers
			TMap<FString, TArray<UPaperSprite*> > SpriteFlipbookMap;

			{
				TArray<UPaperSprite*> Sprites;
				TArray<FString> SpriteNames;

				for (int SpriteIndex = 0; SpriteIndex < SpriteSheet->Sprites.Num(); ++SpriteIndex)
				{
					auto SpriteAssetPtr = SpriteSheet->Sprites[SpriteIndex];
					FStringAssetReference SpriteStringRef = SpriteAssetPtr.ToStringReference();
					UPaperSprite* Sprite = nullptr;
					if (!SpriteStringRef.ToString().IsEmpty())
					{
						Sprite = Cast<UPaperSprite>(StaticLoadObject(UPaperSprite::StaticClass(), nullptr, *SpriteStringRef.ToString(), nullptr, LOAD_None, nullptr));
					}
					if (Sprite != nullptr)
					{
						const FString SpriteName = useSpriteNames ? SpriteSheet->SpriteNames[SpriteIndex] : Sprite->GetName();
						Sprites.Add(Sprite);
						SpriteNames.Add(SpriteName);
					}
				}

				FPaperFlipbookHelpers::ExtractFlipbooksFromSprites(/*out*/ SpriteFlipbookMap, Sprites, SpriteNames);
			}

			// Create one flipbook for every grouped flipbook name
			if (SpriteFlipbookMap.Num() > 0)
			{
				UPaperFlipbookFactory* FlipbookFactory = NewObject<UPaperFlipbookFactory>();

				GWarn->BeginSlowTask(NSLOCTEXT("Paper2D", "Paper2D_CreateFlipbooks", "Creating flipbooks from selection"), true, true);

				int Progress = 0;
				int TotalProgress = SpriteFlipbookMap.Num();
				TArray<UObject*> ObjectsToSync;
				for (auto It : SpriteFlipbookMap)
				{
					GWarn->UpdateProgress(Progress++, TotalProgress);

					const FString& FlipbookName = It.Key;
					TArray<UPaperSprite*>& Sprites = It.Value;

					const FString TentativePackagePath = PackageTools::SanitizePackageName(PackagePath + TEXT("/") + FlipbookName);
					FString DefaultSuffix;
					FString AssetName;
					FString PackageName;
					AssetToolsModule.Get().CreateUniqueAssetName(TentativePackagePath, DefaultSuffix, /*out*/ PackageName, /*out*/ AssetName);

					FlipbookFactory->KeyFrames.Empty();
					for (int32 SpriteIndex = 0; SpriteIndex < Sprites.Num(); ++SpriteIndex)
					{
						FPaperFlipbookKeyFrame* KeyFrame = new (FlipbookFactory->KeyFrames) FPaperFlipbookKeyFrame();
						KeyFrame->Sprite = Sprites[SpriteIndex];
						KeyFrame->FrameRun = 1;
					}

					if (UObject* NewAsset = AssetToolsModule.Get().CreateAsset(AssetName, PackagePath, UPaperFlipbook::StaticClass(), FlipbookFactory))
					{
						ObjectsToSync.Add(NewAsset);
					}

					if (GWarn->ReceivedUserCancel())
					{
						break;
					}
				}

				GWarn->EndSlowTask();

				if (ObjectsToSync.Num() > 0)
				{
					ContentBrowserModule.Get().SyncBrowserToAssets(ObjectsToSync);
				}
			}
		}
	}
}