commit 80f5bbdd8d754a2437fca583523c254092adbd1f Author: Respirant Date: Sat Dec 26 09:55:27 2020 +0300 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a321010 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Respirant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bfde0f --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +![GitHub](https://img.shields.io/github/license/Respirant/RuntimeFilesDownloader) +# RuntimeFilesDownloader +This plugin will be useful for those who want to be able to download any files via HTTP protocol on runtime and save in device storage. It's easy to use in both C++ and Blueprints. + +![Runtime Files Downloader Unreal Engine Plugin Logo](image/runtimeaudioimporter.png "RuntimeFilesDownloader Unreal Engine Plugin Logo") + +## Features +- Low library size (≈ 30 kb) +- Fast downloading speed (780 kb file - 28 sec to download - 4.7 Mbps network speed) +- No any third party libraries and external dependencies +- Support for all available devices (tested on Windows and Android, but there are no restrictions to work on other devices) + +![Runtime Files Downloader Unreal Engine Plugin Nodes 1](image/nodesexample1.png "RuntimeFilesDownloader Unreal Engine Plugin Nodes 1") +![Runtime Files Downloader Unreal Engine Plugin Nodes 2](image/nodesexample2.png "RuntimeFilesDownloader Unreal Engine Plugin Nodes 2") + +## How to install +There're two ways to install the plugin: +1) [Through the marketplace (not ready yet)](https://www.unrealengine.com/marketplace/product/runtime-files-downloader). +2) Manual installation. Just extract the "RuntimeFilesDownloader" folder into your plugins project folder to get the following path: "[ProjectName]/Plugins/RuntimeFilesDownloader". + +## How to use + There are two ways to use this plugin: using Blueprints and C++: + 1. Blueprints. Just reopen your Unreal project and use "Create Downloader" and "Download File" nodes. + 2. C++. Open your "[ProjectName].Build.cs" and find a block of ` PublicDependencyModuleNames ` or ` PrivateDependencyModuleNames ` (it depends on your needs). Add `"RuntimeFilesDownloader"` String inside this block. After just include "RuntimeAudioImporterBPLibrary.h" using ` #include "RuntimeFilesDownloaderBPLibrary.h" ` where needed. + +## Legal info + +Unreal® is a trademark or registered trademark of Epic Games, Inc. in the United States of America and elsewhere. + +Unreal® Engine, Copyright 1998 – 2020, Epic Games, Inc. All rights reserved. diff --git a/RuntimeFilesDownloader/Resources/Icon128.png b/RuntimeFilesDownloader/Resources/Icon128.png new file mode 100644 index 0000000..341ff27 Binary files /dev/null and b/RuntimeFilesDownloader/Resources/Icon128.png differ diff --git a/RuntimeFilesDownloader/RuntimeFilesDownloader.uplugin b/RuntimeFilesDownloader/RuntimeFilesDownloader.uplugin new file mode 100644 index 0000000..7549d80 --- /dev/null +++ b/RuntimeFilesDownloader/RuntimeFilesDownloader.uplugin @@ -0,0 +1,39 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "RuntimeFilesDownloader", + "Description": "Runtime Files Downloader for Unreal Engine. Allows you to download any files via HTTP protocol on runtime and save in device storage", + "Category": "Code Plugins", + "CreatedBy": "Respirant", + "CreatedByURL": "https://github.com/Respirant/RuntimeFilesDownloader", + "DocsURL": "https://github.com/Respirant/RuntimeFilesDownloader", + "MarketplaceURL": "", + "SupportURL": "https://telegram.me/Respirant", + "CanContainContent": false, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": true, + "Modules": [ + { + "Name": "RuntimeFilesDownloader", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Win32", + "Mac", + "Android", + "IOS", + "Linux" + ], + "BlacklistPlatforms": [ + "XboxOne", + "PS4", + "HTML5", + "TVOS", + "Switch" + ] + } + ] +} diff --git a/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Private/RuntimeFilesDownloader.cpp b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Private/RuntimeFilesDownloader.cpp new file mode 100644 index 0000000..10da91e --- /dev/null +++ b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Private/RuntimeFilesDownloader.cpp @@ -0,0 +1,22 @@ +// Respirant 2020. + +#include "RuntimeFilesDownloader.h" + +#define LOCTEXT_NAMESPACE "FRuntimeFilesDownloaderModule" + +void FRuntimeFilesDownloaderModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + +} + +void FRuntimeFilesDownloaderModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRuntimeFilesDownloaderModule, RuntimeFilesDownloader) \ No newline at end of file diff --git a/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Private/RuntimeFilesDownloaderBPLibrary.cpp b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Private/RuntimeFilesDownloaderBPLibrary.cpp new file mode 100644 index 0000000..d7f2ebc --- /dev/null +++ b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Private/RuntimeFilesDownloaderBPLibrary.cpp @@ -0,0 +1,101 @@ +// Respirant 2020. + +#include "RuntimeFilesDownloaderBPLibrary.h" +#include "RuntimeFilesDownloader.h" + +URuntimeFilesDownloaderBPLibrary::URuntimeFilesDownloaderBPLibrary(const FObjectInitializer& ObjectInitializer) +: Super(ObjectInitializer) +{ + +} + + +URuntimeFilesDownloaderBPLibrary::URuntimeFilesDownloaderBPLibrary() : + FileUrl(TEXT("")) + , FileSavePath(TEXT("")) +{ +} + +URuntimeFilesDownloaderBPLibrary::~URuntimeFilesDownloaderBPLibrary() +{ +} + +URuntimeFilesDownloaderBPLibrary* URuntimeFilesDownloaderBPLibrary::MakeDownloader() +{ + URuntimeFilesDownloaderBPLibrary* Downloader = NewObject(); + return Downloader; +} + + +URuntimeFilesDownloaderBPLibrary* URuntimeFilesDownloaderBPLibrary::DownloadFile(const FString& Url, FString SavePath) +{ + URuntimeFilesDownloaderBPLibrary* Downloader = NewObject(); + FileUrl = Url; + FileSavePath = SavePath; + + TSharedRef< IHttpRequest, ESPMode::ThreadSafe > HttpRequest = FHttpModule::Get().CreateRequest(); + + HttpRequest->SetVerb("GET"); + HttpRequest->SetURL(Url); + HttpRequest->OnProcessRequestComplete().BindUObject(this, &URuntimeFilesDownloaderBPLibrary::OnReady); + HttpRequest->OnRequestProgress().BindUObject(this, &URuntimeFilesDownloaderBPLibrary::OnProgress_Internal); + + // Execute the request + HttpRequest->ProcessRequest(); + AddToRoot(); + + return this; + + +} + +void URuntimeFilesDownloaderBPLibrary::OnReady(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + RemoveFromRoot(); + Request->OnProcessRequestComplete().Unbind(); + + if (Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) + { + // Save file + IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); + + // Create save directory if not existent + FString Path, Filename, Extension; + FPaths::Split(FileSavePath, Path, Filename, Extension); + if (!PlatformFile.DirectoryExists(*Path)) + { + if (!PlatformFile.CreateDirectoryTree(*Path)) + { + OnResult.Broadcast(EDownloadResult::DirectoryCreationFailed); + return; + } + } + + // Open/Create the file + IFileHandle* FileHandle = PlatformFile.OpenWrite(*FileSavePath); + if (FileHandle) + { + // Write the file + FileHandle->Write(Response->GetContent().GetData(), Response->GetContentLength()); + // Close the file + delete FileHandle; + + OnResult.Broadcast(EDownloadResult::Success); + } + else + { + OnResult.Broadcast(EDownloadResult::SaveFailed); + } + } + else + { + OnResult.Broadcast(EDownloadResult::DownloadFailed); + } +} + +void URuntimeFilesDownloaderBPLibrary::OnProgress_Internal(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived) +{ + int32 FullSize = Request->GetContentLength(); + OnProgress.Broadcast(BytesSent, BytesReceived, FullSize); +} + diff --git a/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Public/RuntimeFilesDownloader.h b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Public/RuntimeFilesDownloader.h new file mode 100644 index 0000000..68903f1 --- /dev/null +++ b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Public/RuntimeFilesDownloader.h @@ -0,0 +1,12 @@ +// Respirant 2020. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FRuntimeFilesDownloaderModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Public/RuntimeFilesDownloaderBPLibrary.h b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Public/RuntimeFilesDownloaderBPLibrary.h new file mode 100644 index 0000000..b660b31 --- /dev/null +++ b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/Public/RuntimeFilesDownloaderBPLibrary.h @@ -0,0 +1,80 @@ +// Respirant 2020. + +#pragma once +#include "Http.h" + +#include "Misc/Paths.h" +#include "HAL/PlatformFilemanager.h" + +#include "Kismet/BlueprintFunctionLibrary.h" +#include "RuntimeFilesDownloaderBPLibrary.generated.h" + +UENUM(BlueprintType, Category = "RuntimeFilesDownloader") +enum class EDownloadResult : uint8 +{ + Success, + DownloadFailed, + SaveFailed, + DirectoryCreationFailed +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnResult, const EDownloadResult, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnProgress, const int32, BytesSent, const int32, BytesReceived, const int32, ContentLength); + +UCLASS() +class RUNTIMEFILESDOWNLOADER_API URuntimeFilesDownloaderBPLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_UCLASS_BODY() + +public: + /** + * Bind to know when the download is complete (even if it fails). + */ + UPROPERTY(BlueprintAssignable, Category = "RuntimeFilesDownloader") + FOnResult OnResult; + /** + * Bind to know when the download is complete (even if it fails). + */ + UPROPERTY(BlueprintAssignable, Category = "RuntimeFilesDownloader") + FOnProgress OnProgress; + + /** + * The URL used to start this download. + */ + UPROPERTY(BlueprintReadOnly, Category = "RuntimeFilesDownloader") + FString FileUrl; + /** + * The path set to save the downloaded file. + */ + UPROPERTY(BlueprintReadOnly, Category = "RuntimeFilesDownloader") + FString FileSavePath; + + URuntimeFilesDownloaderBPLibrary(); + ~URuntimeFilesDownloaderBPLibrary(); + + + /** + * Instantiates a FileDownloader object, starts downloading and saves it when done. + * + * @return The FileDownloader object. Bind to it's OnResult event to know when it's done downloading. + */ + UFUNCTION(BlueprintCallable, Meta = (DisplayName = "Create Downloader"), Category = "RuntimeFilesDownloader") + static URuntimeFilesDownloaderBPLibrary* MakeDownloader(); + + /** + * Starts downloading a file and saves it when done. Bind to the OnResult + * event to know when the download is done (preferrably, before calling this function). + * + * @param Url The file Url to be downloaded. + * @param SavePath The absolute path and file name to save the downloaded file. + * @return Returns itself. + */ + UFUNCTION(BlueprintCallable, Meta = (DisplayName = "Download File"), Category = "RuntimeFilesDownloader") + URuntimeFilesDownloaderBPLibrary* DownloadFile(const FString & Url, FString SavePath); + +private: + + void OnReady(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + void OnProgress_Internal(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived); +}; + diff --git a/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/RuntimeFilesDownloader.Build.cs b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/RuntimeFilesDownloader.Build.cs new file mode 100644 index 0000000..8e52817 --- /dev/null +++ b/RuntimeFilesDownloader/Source/RuntimeFilesDownloader/RuntimeFilesDownloader.Build.cs @@ -0,0 +1,34 @@ +// Respirant 2020. + +using UnrealBuildTool; + +public class RuntimeFilesDownloader : ModuleRules +{ + public RuntimeFilesDownloader(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "HTTP", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + // ... add private dependencies that you statically link with here ... + } + ); + + } +} diff --git a/image/nodesexample1.png b/image/nodesexample1.png new file mode 100644 index 0000000..8f6072a Binary files /dev/null and b/image/nodesexample1.png differ diff --git a/image/nodesexample2.png b/image/nodesexample2.png new file mode 100644 index 0000000..64f6331 Binary files /dev/null and b/image/nodesexample2.png differ diff --git a/image/runtimefilesdownloader.png b/image/runtimefilesdownloader.png new file mode 100644 index 0000000..1d63a24 Binary files /dev/null and b/image/runtimefilesdownloader.png differ