void FStreamableManager::AsyncLoadCallback(FStringAssetReference Request)
{
	FStringAssetReference TargetName = Request;
	FStreamable* Existing = StreamableItems.FindRef(TargetName);

	UE_LOG(LogStreamableManager, Verbose, TEXT("Stream Complete callback %s"), *TargetName.AssetLongPathname);
	if (!Existing)
	{
		// hmm, maybe it was redirected by a consolidate
		TargetName = ResolveRedirects(TargetName);
		FStreamable* 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.AssetLongPathname);
	}
}
struct FStreamable* FStreamableManager::StreamInternal(FStringAssetReference const& InTargetName)
{
	check(IsInGameThread());
	UE_LOG(LogStreamableManager, Verbose, TEXT("Asynchronous load %s"), *InTargetName.AssetLongPathname);

	if (FPackageName::IsShortPackageName(InTargetName.AssetLongPathname))
	{
		UE_LOG(LogStreamableManager, Error, TEXT("     Can't load invalid package name %s"), *InTargetName.AssetLongPathname);
		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.AssetLongPathname);
			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.AssetLongPathname);
			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.AssetLongPathname);
			return Existing; 
		}
	}
	else
	{
		Existing = StreamableItems.Add(TargetName, new FStreamable());
	}

	FindInMemory(TargetName, Existing);

	if (!Existing->Target)
	{
		FString Package = TargetName.AssetLongPathname;
		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;
}
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;
}
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;
}
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());
	}
}
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;
}