Skip to content

Instantly share code, notes, and snippets.

@emrahgunduz
Created November 14, 2016 12:55
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save emrahgunduz/c527105e752d705d03dc5a8ac38cebbe to your computer and use it in GitHub Desktop.
Save emrahgunduz/c527105e752d705d03dc5a8ac38cebbe to your computer and use it in GitHub Desktop.
Package Downloader And Mounter For Unreal Engine 4 -- Do not forget to correct the include and api name if you are copy pasting this code
#include "HitMe.h"
#include "PackageDownloader.h"
#include "IPlatformFilePak.h"
#include "HitMeSingleton.h"
UPackageDownloader* UPackageDownloader::GetPackageDownloader(FString PackageName, FString URL, bool& IsValid)
{
IsValid = false;
UPackageDownloader *Object = NewObject<UPackageDownloader>();
if (!Object) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::GetPackageDownloader] Could not be created"));
return NULL;
}
if (!Object->IsValidLowLevel()) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::GetPackageDownloader] Created object is not valid"));
return NULL;
}
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::GetPackageDownloader] An instance is created successfully"));
Object->OriginalPackageName = FString(PackageName);
Object->OriginalURL = FString(URL);
IsValid = true;
return Object;
}
FString UPackageDownloader::PackageFolder()
{
FString FileDir = FPaths::GamePersistentDownloadDir() + "/DownPaks/";
FPaths::NormalizeDirectoryName(FileDir);
return FString(FPaths::ConvertRelativePathToFull(FileDir));
}
bool UPackageDownloader::CreatePackageFolder()
{
FDirPackageRecursiveDownloader RFolder = FDirPackageRecursiveDownloader::CreateLambda([=](FString Folder)
{
const int32 MAX_LOOP_ITR = 3000;
FPaths::NormalizeDirectoryName(Folder);
Folder += "/";
FString Base;
FString Left;
FString Remaining;
Folder.Split(TEXT("/"), &Base, &Remaining);
Base += "/";
int32 LoopItr = 0;
while (Remaining != "" && LoopItr < MAX_LOOP_ITR)
{
Remaining.Split(TEXT("/"), &Left, &Remaining);
Base += Left + FString("/");
FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*Base);
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::CreatePackageFolder] Creating %s"), *Base);
LoopItr++;
}
});
FString FileDir = PackageFolder();
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (!PlatformFile.DirectoryExists(*FileDir)) {
RFolder.Execute(FileDir);
if (!PlatformFile.DirectoryExists(*FileDir)) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::CreatePackageFolder] Cannot create folder %s"), *FileDir);
return false;
}
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::CreatePackageFolder] Created folder %s"), *FileDir);
}
return true;
}
TArray<FString> UPackageDownloader::DownloadedPackagesList()
{
FString folder = PackageFolder() + "/*.*";
IFileManager& FileManager = IFileManager::Get();
TArray<FString>files;
FileManager.FindFiles(files, *folder, true, false);
for (int i = 0; i < files.Num(); i++) {
FString str = files[i];
files[i] = str.Replace(TEXT(".pak"), TEXT(""), ESearchCase::IgnoreCase);
}
return files;
}
void UPackageDownloader::IsPackageDownloaded(FString PackageName, bool& isDownloaded)
{
if (!CreatePackageFolder())
{
isDownloaded = false;
return;
}
FString dataFile = PackageFolder() + "/" + PackageName + ".pak";
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
isDownloaded = PlatformFile.FileExists(*dataFile);
}
void UPackageDownloader::DeletePackageFile(FString PackageName, bool &isDeleted)
{
FString dataFile = PackageFolder() + "/" + PackageName + ".pak";
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (!PlatformFile.FileExists(*dataFile))
{
isDeleted = true;
return;
}
isDeleted = PlatformFile.DeleteFile(*dataFile);
}
void UPackageDownloader::IsPackageMounted(FString PackageName, bool& isMounted)
{
UHitMeSingleton *Object = Cast<UHitMeSingleton>(GEngine->GameSingleton);
if (!(Object && Object->IsValidLowLevel())) {
isMounted = false;
return;
}
isMounted = Object->mountedPackages.Contains(PackageName);
}
void UPackageDownloader::MountPackage(FString PackageName, FString MountFolder, bool& isMounted)
{
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::MountPackage] Mounting package %s"), *PackageName);
FString PackageFile = PackageFolder() + "/" + PackageName + ".pak";
isMounted = false;
bool bSuccessfulInitialization = false;
IPlatformFile* LocalPlatformFile = &FPlatformFileManager::Get().GetPlatformFile();
if (LocalPlatformFile != nullptr)
{
IPlatformFile* PakPlatformFile = FPlatformFileManager::Get().GetPlatformFile(TEXT("PakFile"));
if (PakPlatformFile != nullptr) bSuccessfulInitialization = true;
}
if (bSuccessfulInitialization)
{
const TCHAR* cmdLine = TEXT("");
FPakPlatformFile* PakPlatform = new FPakPlatformFile();
IPlatformFile* InnerPlatform = LocalPlatformFile;
PakPlatform->Initialize(InnerPlatform, cmdLine);
FPlatformFileManager::Get().SetPlatformFile(*PakPlatform);
FPakFile PakFile(*PackageFile, false);
if (!PakFile.IsValid())
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::MountPackage] Invalid pak file %s"), *PackageName);
return;
}
FString MountPoint = FPaths::GameContentDir() + MountFolder + "/" + PackageName;
PakFile.SetMountPoint(*MountPoint);
const int32 PakOrder = 0;
#if WITH_EDITOR
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::MountPackage] Not mounting %s/%s in editor"), *MountPoint, *PackageFile);
#else
if (!PakPlatform->Mount(*PackageFile, PakOrder, *MountPoint))
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::MountPackage] Failed to mount package %s"), *MountPoint);
return;
}
#endif
isMounted = true;
UHitMeSingleton *Object = Cast<UHitMeSingleton>(GEngine->GameSingleton);
if (Object && Object->IsValidLowLevel()) {
Object->mountedPackages.Add(PackageName);
}
// Package location control
TArray<FString> FileList;
PakFile.FindFilesAtPath(FileList, *PakFile.GetMountPoint(), true, false, true);
for (int32 b = 0; b < FileList.Num(); b++) {
FString file = FileList[b];
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::MountPackage] Content %s: %s"), *PackageName, *file);
}
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::MountPackage] Mounted to %s"), *MountPoint);
}
}
void UPackageDownloader::CheckIfPackageHasUpdate()
{
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::DoesPackageHaveUpdate] Checking for update %s"), *OriginalPackageName);
FString dataFile = PackageFolder() + "/" + OriginalPackageName + ".pak";
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
if (!PlatformFile.FileExists(*dataFile))
{
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::DoesPackageHaveUpdate] Package not downloaded yet %s"), *OriginalPackageName);
OnUpdateCheckCompleted.Broadcast(OriginalPackageName, true);
return;
}
RequestSize = -1;
RequestUrl = OriginalURL + "/" + OriginalPackageName + ".pak";
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::DoesPackageHaveUpdate] Requesting headers for %s from %s"), *OriginalPackageName, *RequestUrl);
FHttpModule* Http = &FHttpModule::Get();
TSharedRef<IHttpRequest> HttpRequest = Http->CreateRequest();
UpdateRequest = HttpRequest;
HttpRequest->SetVerb(TEXT("HEAD"));
HttpRequest->SetURL(RequestUrl);
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UPackageDownloader::UpdateCheckHttpRequestComplete);
if (!HttpRequest->ProcessRequest())
{
OnUpdateCheckCompleted.Broadcast(OriginalPackageName, false);
}
}
void UPackageDownloader::UpdateCheckHttpRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
UpdateRequest.Reset();
if (!Response.IsValid()) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::UpdateCheckHttpRequestComplete] Could not connect to %s"), *RequestUrl);
OnUpdateCheckCompleted.Broadcast(OriginalPackageName, false);
return;
}
if (!bWasSuccessful) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::UpdateCheckHttpRequestComplete] Could not connect to %s"), *RequestUrl);
OnUpdateCheckCompleted.Broadcast(OriginalPackageName, false);
return;
}
FString dataFile = PackageFolder() + "/" + OriginalPackageName + ".pak";
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
FFileStatData statData = PlatformFile.GetStatData(*dataFile);
bool isSizeDifferent = false;
bool isModDateDifferent = false;
int64 fileSize = 0;
int64 modDate = 0;
{
int32 StatusCode = Response->GetResponseCode();
if (StatusCode / 100 != 2)
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::UpdateCheckHttpRequestComplete] %s HTTP response %d, for %s"), *OriginalPackageName, StatusCode, *RequestUrl);
OnUpdateCheckCompleted.Broadcast(OriginalPackageName, false);
return;
}
TArray<FString> headers = Response->GetAllHeaders();
for (FString h : headers) {
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::UpdateCheckHttpRequestComplete] %s Header: %s"), *OriginalPackageName, *h);
}
for (FString h : headers) {
if (h.Contains("x-file-size", ESearchCase::IgnoreCase) || h.Contains("Content-Length", ESearchCase::IgnoreCase)) {
FString left;
FString right;
h.Split(":", &left, &right, ESearchCase::IgnoreCase, ESearchDir::FromStart);
if (right.Len())
{
fileSize = FCString::Atoi(*right);
}
}
if (h.Contains("x-file-mod", ESearchCase::IgnoreCase)) {
FString left;
FString right;
h.Split(":", &left, &right, ESearchCase::IgnoreCase, ESearchDir::FromStart);
if (right.Len())
{
modDate = FCString::Atoi(*right);
}
}
if (h.Contains("Last-Modified", ESearchCase::IgnoreCase)) {
FString left;
FString right;
h.Split(":", &left, &right, ESearchCase::IgnoreCase, ESearchDir::FromStart);
if (right.Len())
{
right = right.Trim().TrimTrailing();
FDateTime date;
FDateTime::ParseHttpDate(right, date);
modDate = date.ToUnixTimestamp();
}
}
}
Request.Reset();
}
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::UpdateCheckHttpRequestComplete] %s - REMOTE: File size %i Mod date %i"), *OriginalPackageName, fileSize, modDate);
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::UpdateCheckHttpRequestComplete] %s - LOCAL: File size %i Mod date %i"), *OriginalPackageName, statData.FileSize, statData.ModificationTime.ToUnixTimestamp());
isSizeDifferent = fileSize > 0 && statData.FileSize != fileSize;
isModDateDifferent = modDate > 0 && modDate > statData.ModificationTime.ToUnixTimestamp();
OnUpdateCheckCompleted.Broadcast(OriginalPackageName, isSizeDifferent || isModDateDifferent);
}
void UPackageDownloader::DownloadPackage()
{
CreatePackageFolder();
RequestSize = -1;
RequestUrl = OriginalURL + "/" + OriginalPackageName + ".pak";
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::DownloadPackage] %s Requesting headers for %s"), *OriginalPackageName, *RequestUrl);
FHttpModule* Http = &FHttpModule::Get();
TSharedRef<IHttpRequest> HttpRequest = Http->CreateRequest();
DownloadRequest = HttpRequest;
HttpRequest->SetVerb(TEXT("HEAD"));
HttpRequest->SetURL(RequestUrl);
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UPackageDownloader::HttpRequestComplete);
if (!HttpRequest->ProcessRequest())
{
OnPackageDownloadError.Broadcast(OriginalPackageName);
}
}
void UPackageDownloader::HttpRequestProgress(FHttpRequestPtr Request, int32 bytesSent, int32 bytesReceived)
{
if (RequestSize <= 0) return;
float percent = (float)bytesReceived / (float)RequestSize;
OnPackageDownloadProgress.Broadcast(OriginalPackageName, (int32)(percent * 100));
}
void UPackageDownloader::HttpRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
DownloadRequest.Reset();
if (!Response.IsValid()) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpRequestComplete] Could not connect to %s"), *RequestUrl);
OnPackageDownloadError.Broadcast(OriginalPackageName);
return;
}
if (!bWasSuccessful) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpRequestComplete] Could not connect to %s"), *RequestUrl);
OnPackageDownloadError.Broadcast(OriginalPackageName);
return;
}
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::HttpRequestComplete] %s Starting download of %s"), *OriginalPackageName, *RequestUrl);
// Finding size of the requested file
{
int32 StatusCode = Response->GetResponseCode();
if (StatusCode / 100 != 2)
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpRequestComplete] %s HTTP response %d, for %s"), *OriginalPackageName, StatusCode, *RequestUrl);
OnPackageDownloadError.Broadcast(OriginalPackageName);
return;
}
TArray<FString> headers = Response->GetAllHeaders();
for (FString h : headers) {
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::HttpRequestComplete] %s Header: %s"), *OriginalPackageName, *h);
}
for (FString h : headers) {
if (h.Contains("x-file-size", ESearchCase::IgnoreCase) || h.Contains("Content-Length", ESearchCase::IgnoreCase)) {
FString left;
FString right;
h.Split(":", &left, &right, ESearchCase::IgnoreCase, ESearchDir::FromStart);
if (right.Len())
{
RequestSize = FCString::Atoi(*right);
}
break;
}
}
Request.Reset();
}
FHttpModule* Http = &FHttpModule::Get();
TSharedRef<IHttpRequest> HttpRequest = Http->CreateRequest();
DownloadRequest = HttpRequest;
HttpRequest->SetVerb(TEXT("GET"));
HttpRequest->SetURL(RequestUrl);
HttpRequest->OnRequestProgress().BindUObject(this, &UPackageDownloader::HttpRequestProgress);
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UPackageDownloader::HttpDownloadComplete);
if (!HttpRequest->ProcessRequest())
{
OnPackageDownloadError.Broadcast(OriginalPackageName);
}
}
void UPackageDownloader::HttpDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
DownloadRequest.Reset();
if (!Response.IsValid()) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpDownloadComplete] Could not connect to %s"), *RequestUrl);
OnPackageDownloadError.Broadcast(OriginalPackageName);
return;
}
if (!bWasSuccessful) {
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpDownloadComplete] Could not connect to %s"), *RequestUrl);
OnPackageDownloadError.Broadcast(OriginalPackageName);
return;
}
UE_LOG(HITMELOG, Warning, TEXT("[UPackageDownloader::HttpDownloadComplete] Download completed for %s from %s"), *OriginalPackageName, *RequestUrl);
int32 StatusCode = Response->GetResponseCode();
if (StatusCode / 100 != 2)
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpDownloadComplete] %s HTTP response %d, for %s"), *OriginalPackageName, StatusCode, *RequestUrl);
OnPackageDownloadError.Broadcast(OriginalPackageName);
return;
}
TArray<FString> headers = Response->GetAllHeaders();
for (FString h : headers) {
UE_LOG(HITMELOG, Warning, TEXT("UExpoSocket::HttpDownloadComplete] %s Header: %s"), *OriginalPackageName, *h);
}
const TArray<uint8>& Content = Response->GetContent();
FString Filename = PackageFolder() + "/" + OriginalPackageName + ".pak";
if (FFileHelper::SaveArrayToFile(Content, *Filename))
{
OnPackageDownloadCompleted.Broadcast(OriginalPackageName);
}
else
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::HttpDownloadComplete] %s Could not write %s to disk "), *OriginalPackageName, *Filename);
OnPackageDownloadError.Broadcast(OriginalPackageName);
}
}
void UPackageDownloader::CancelDownload()
{
UE_LOG(HITMELOG, Error, TEXT("[UPackageDownloader::CancelDownload] Cancelling request for %s"), *RequestUrl);
if (UpdateRequest.IsValid()) {
if (UpdateRequest->OnProcessRequestComplete().IsBound())
UpdateRequest->OnProcessRequestComplete().Unbind();
UpdateRequest->CancelRequest();
UpdateRequest.Reset();
}
if (DownloadRequest.IsValid()) {
if (DownloadRequest->OnProcessRequestComplete().IsBound())
DownloadRequest->OnProcessRequestComplete().Unbind();
DownloadRequest->CancelRequest();
DownloadRequest.Reset();
}
}
#pragma once
#include "Object.h"
#include "Runtime/Online/HTTP/Public/Http.h"
#include "Runtime/Online/HTTP/Public/HttpManager.h"
#include "PackageDownloader.generated.h"
DECLARE_DELEGATE_OneParam(FDirPackageRecursiveDownloader, FString);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPackageDownloader_OnDownloadComplete, FString, mapName);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FPackageDownloader_OnDownloadError, FString, mapName);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FPackageDownloader_OnDownloadProgress, FString, mapName, int32, progress);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FPackageDownloader_OnUpdateCheckCompleted, FString, mapName, bool, hasUpdate);
UCLASS(Blueprintable, BlueprintType)
class HITME_API UPackageDownloader : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category = "Package Downloader", Meta = (DisplayName = "Get A Package Downloader"))
static UPackageDownloader* GetPackageDownloader(FString PackageName, FString URL, bool& IsValid);
UFUNCTION(BlueprintPure, Category = "Package Downloader", Meta = (DisplayName = "Downloaded Packages List"))
static TArray<FString> DownloadedPackagesList();
UFUNCTION(BlueprintPure, Category = "Package Downloader", Meta = (DisplayName = "Delete Package File"))
static void DeletePackageFile(FString PackageName, bool &isDeleted);
UFUNCTION(BlueprintPure, Category = "Package Downloader", Meta = (DisplayName = "Is Package Already Downloaded"))
static void IsPackageDownloaded(FString PackageName, bool &isDownloaded);
UFUNCTION(BlueprintPure, Category = "Package Downloader", Meta = (DisplayName = "Is Package Already Mounted"))
static void IsPackageMounted(FString PackageName, bool &isMounted);
UFUNCTION(BlueprintPure, Category = "Package Downloader", Meta = (DisplayName = "Mount Package"))
static void MountPackage(FString PackageName, FString MountFolder, bool& isMounted);
UPROPERTY(BlueprintAssignable, Category = "Package Downloader")
FPackageDownloader_OnDownloadComplete OnPackageDownloadCompleted;
UPROPERTY(BlueprintAssignable, Category = "Package Downloader")
FPackageDownloader_OnDownloadError OnPackageDownloadError;
UPROPERTY(BlueprintAssignable, Category = "Package Downloader")
FPackageDownloader_OnDownloadProgress OnPackageDownloadProgress;
UPROPERTY(BlueprintAssignable, Category = "Package Downloader")
FPackageDownloader_OnUpdateCheckCompleted OnUpdateCheckCompleted;
UFUNCTION(BlueprintCallable, Category = "Package Downloader", Meta = (DisplayName = "Does Package Have Update"))
void CheckIfPackageHasUpdate();
UFUNCTION(BlueprintCallable, Category = "Package Downloader", Meta = (DisplayName = "Download Package"))
void DownloadPackage();
UFUNCTION(BlueprintCallable, Category = "Package Downloader", Meta = (DisplayName = "Cancel Download"))
void CancelDownload();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Package Downloader", Meta = (DisplayName = "Package Name"))
FString OriginalPackageName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Package Downloader", Meta = (DisplayName = "Server URL"))
FString OriginalURL;
static FString PackageFolder();
static bool CreatePackageFolder();
private:
void HttpRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
void HttpDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
void HttpRequestProgress(FHttpRequestPtr Request, int32 bytesSent, int32 bytesReceived);
void UpdateCheckHttpRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
int32 RequestSize;
FString RequestUrl;
TSharedPtr<IHttpRequest> UpdateRequest;
TSharedPtr<IHttpRequest> DownloadRequest;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment