diff --git a/Assets/Editor/AssetStoreBatchMode/AssetStoreBatchMode.cs b/Assets/Editor/AssetStoreBatchMode/AssetStoreBatchMode.cs deleted file mode 100644 index d1b6c668..00000000 --- a/Assets/Editor/AssetStoreBatchMode/AssetStoreBatchMode.cs +++ /dev/null @@ -1,676 +0,0 @@ -using System; -using System.Collections; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using Thinksquirrel.Development.Integration; -using UnityEditor; -using UnityEngine; -using System.Collections.Generic; -using NDesk.Options; -using Debug = UnityEngine.Debug; - -#region Main class -public static class AssetStoreBatchMode -{ - // Input - static string s_Username; - static string s_Password; - static string s_PackageName; - static string s_RootPath; - // ReSharper disable once NotAccessedField.Local - #pragma warning disable 414 - static string[] s_MainAssets; // TODO - #pragma warning restore 414 - static int s_LoginTimeout; - static int s_MetadataTimeout; - static int s_UploadTimeout; - static bool s_SkipProjectSettings; - - static readonly AssetStorePublisher s_PublisherAccount = new AssetStorePublisher(); - static readonly PackageDataSource s_PackageDataSource = new PackageDataSource(); - - static bool s_LoginDone; - static bool s_GetMetadataDone; - static bool s_AssetsUploadedDone; - static readonly Stopwatch s_Stopwatch = new Stopwatch(); - - /// - /// Upload a package, using the command line arguments of the current environment. - /// - /// - /// The Asset Store account password must be provided via the "ASSET_STORE_PASSWORD" environment variable. - /// - public static void UploadAssetStorePackage() - { - UploadAssetStorePackage(Environment.GetCommandLineArgs()); - } - - /// - /// Upload a package, using the specified command line arguments. - /// - /// - /// The Asset Store account password must be provided via the "ASSET_STORE_PASSWORD" environment variable. - /// - public static void UploadAssetStorePackage(params string[] args) - { - var username = Environment.GetEnvironmentVariable("ASSET_STORE_USERNAME"); - var password = Environment.GetEnvironmentVariable("ASSET_STORE_PASSWORD"); - var packageName = Environment.GetEnvironmentVariable("ASSET_STORE_PACKAGE_NAME"); - var rootPath = Environment.GetEnvironmentVariable("ASSET_STORE_ROOT_PATH"); - var loginTimeout = 10; - var metadataTimeout = 300; - var uploadTimeout = 36000; - var skipProjectSettings = false; - - var mainAssets = new List(); - - var mainAssetsStr = Environment.GetEnvironmentVariable("ASSET_STORE_MAIN_ASSETS"); - if (mainAssetsStr != null) - { - var mainAssetsSplit = mainAssetsStr.Split(':'); - for(var i = 0; i < mainAssetsSplit.Length; ++i) - { - mainAssets.Add(mainAssetsSplit[i]); - } - } - - var assets = mainAssets; - var opt = new OptionSet - { - { "asset_store_username=", - "The username credentials to use for package uploading.", - o => username = o }, - - { "asset_store_package_name=", - "The package name. The package must be set to draft status in the Publisher Administration.", - o => packageName = o }, - - { "asset_store_root_path=", - "The root path of the package (relative to Application.dataPath). If not present, use the project Assets folder.", - o => rootPath = o }, - - { "asset_store_main_asset=", - "A main asset for the package (relative to Application.dataPath). Multiple options are allowed. If not present, do not upload or change any main assets.", - assets.Add }, - - { "asset_store_login_timeout=", - "The maximum amount of time to wait (in seconds) when logging in. Defaults to 10 seconds. Must be within 2 and 36000 seconds. Login is attempted twice.", - (int o) => loginTimeout = o }, - - { "asset_store_metadata_timeout=", - "The maximum amount of time to wait (in seconds) when getting metadata. Defaults to 300 seconds. Must be within 2 and 36000 seconds.", - (int o) => metadataTimeout = o }, - - { "asset_store_upload_timeout=", - "The maximum amount of time to wait (in seconds) when uploading. Defaults to 36000 seconds. Must be within 2 and 36000 seconds.", - (int o) => uploadTimeout = o }, - - { "skip_project_settings", - "If true, always skip project settings export. This only applies to assets in the Complete Projects category.", - o => skipProjectSettings = o != null } - }; - - opt.Parse(args); - - UploadAssetStorePackage(username, password, packageName, new []{rootPath}, mainAssets.ToArray(), loginTimeout, metadataTimeout, uploadTimeout, skipProjectSettings); - } - /// - /// Upload a package, using the specified options. - /// - /// The username credentials to use for package uploading. - /// The password credentials to use for package uploading. - /// The package name. The package must be set to draft status in the Publisher Administration. - /// The root path of the package (relative to Application.dataPath). If null, use the project Assets folder. - /// An array of the main assets for the package (relative to Application.dataPath). If null, do not upload or change any main assets. - /// The maximum amount of time to wait (in seconds) when logging in. Defaults to 90 seconds. Must be within 2 and 36000 seconds. Login is attempted twice. - /// The maximum amount of time to wait (in seconds) when getting metadata. Defaults to 600 seconds. Must be within 2 and 36000 seconds. - /// The maximum amount of time to wait (in seconds) when uploading. Defaults to 36000 seconds. Must be within 2 and 36000 seconds. - public static void UploadAssetStorePackage(string username, string password, string packageName, string[] rootPaths = null, string[] mainAssets = null, int loginTimeout = 90, int metadataTimeout = 600, int uploadTimeout = 36000, bool skipProjectSettings = false) - { - if (string.IsNullOrEmpty(username)) - throw new ArgumentNullException("username"); - - if (string.IsNullOrEmpty(password)) - throw new ArgumentNullException("password"); - - if (string.IsNullOrEmpty(packageName)) - throw new ArgumentNullException("packageName"); - - s_Username = username; - s_Password = password; - s_PackageName = packageName; - s_RootPath = rootPaths[0]; - s_MainAssets = mainAssets; - s_LoginTimeout = Mathf.Clamp(loginTimeout, 2, 36000); - s_MetadataTimeout = Mathf.Clamp(metadataTimeout, 2, 36000); - s_UploadTimeout = Mathf.Clamp(uploadTimeout, 2, 36000); - s_SkipProjectSettings = skipProjectSettings; - - Finish(); - -#if !UNITY_5_5_OR_NEWER - if (Application.webSecurityEnabled) - { - Debug.Log("[Asset Store Batch Mode] Switching from Web Player platform..."); - - EditorUserBuildSettings.SwitchActiveBuildTarget(EditorUserBuildSettings.selectedStandaloneTarget); - } -#endif - - Debug.Log("[Asset Store Batch Mode] Logging into the Asset Store..."); - - AssetStoreClient.LoginWithCredentials(s_Username, s_Password, false, OnLogin); - - if (!WaitForUpdate(ref s_LoginDone, s_LoginTimeout)) - { - Finish(); - - // Try again - s_LoginDone = false; - AssetStoreClient.LoginWithCredentials(s_Username, s_Password, false, OnLogin); - - if (!WaitForUpdate(ref s_LoginDone, s_LoginTimeout)) - { - Finish(); - return; - } - } - - AssetStoreAPI.GetMetaData(s_PublisherAccount, s_PackageDataSource, OnGetMetadata); - - Debug.Log("[Asset Store Batch Mode] Getting package metadata..."); - - if (!WaitForUpdate(ref s_GetMetadataDone, s_MetadataTimeout)) - { - Finish(); - return; - } - - var packages = s_PackageDataSource.GetAllPackages(); - var package = packages.FirstOrDefault(p => p.Name == s_PackageName && p.Status == Package.PublishedStatus.Draft); - - if (package == null) - { - Debug.LogError("[Asset Store Batch Mode] Draft package: " + s_PackageName + " not found!"); - Finish(); - return; - } - - // Validate root project folder - List projectFolders = new List(); - foreach (string rootPath in rootPaths) - { - string projectFolder = Path.Combine(Application.dataPath, rootPath ?? string.Empty); - if (!IsValidProjectFolder(projectFolder)) - { - Debug.LogError("[Asset Store Batch Mode] Project folder is invalid"); - Finish(); - return; - } - projectFolders.Add(projectFolder); - } - - // Set root asset path - List localRootPaths = new List(); - foreach (string projectFolder in projectFolders) - { - string localRootPath = SetRootPath(package, projectFolder); - localRootPaths.Add(localRootPath); - - // Verify content - var checkContent = CheckContent(package, localRootPath); - if (!string.IsNullOrEmpty(checkContent)) - { - Debug.LogError("[Asset Store Batch Mode] " + checkContent); - Finish(); - return; - } - } - - // Set main assets - if (MainAssetsUtil.CanGenerateBundles) - { - // TODO: Set main assets - } - - var draftAssetsPath = GetDraftAssetsPath(localRootPaths[0]); - - Export(package, localRootPaths, draftAssetsPath); - - // Upload assets - AssetStoreAPI.UploadAssets( - package, - AssetStorePackageController.GetLocalRootGUID(package), - localRootPaths[0], - Application.dataPath, - draftAssetsPath, - OnAssetsUploaded, null); - - Debug.Log("[Asset Store Batch Mode] Uploading asset..."); - - if (!WaitForUpdate(ref s_AssetsUploadedDone, s_UploadTimeout)) - { - Finish(); - return; - } - - if (MainAssetsUtil.CanGenerateBundles) - { - // TODO: Upload main assets - } - - Debug.Log("[Asset Store Batch Mode] Asset successfully uploaded"); - - Finish(); - } - - - static void OnLogin(string errorMessage) - { - s_LoginDone = true; - - if (errorMessage == null) return; - - Debug.LogError("[Asset Store Batch Mode] " + errorMessage); - Finish(); - } - - static void OnGetMetadata(string errorMessage) - { - s_GetMetadataDone = true; - - if (errorMessage == null) return; - - Debug.LogError("[Asset Store Batch Mode] " + errorMessage); - Finish(); - } - - static void OnAssetsUploaded(string errorMessage) - { - s_AssetsUploadedDone = true; - - if (errorMessage == null) return; - - Debug.LogError("[Asset Store Batch Mode] " + errorMessage); - Finish(); - } - - // ----------------------------------------------- - // Helper functions - // ----------------------------------------------- - - static void Finish() - { - AssetStoreClient.Logout(); - s_LoginDone = false; - s_GetMetadataDone = false; - s_AssetsUploadedDone = false; - AssetStoreClient.Update(); - } - static bool WaitForUpdate(ref bool isDone, int timeout) - { - s_Stopwatch.Reset(); - s_Stopwatch.Start(); - - do - { - AssetStoreClient.Update(); - Thread.Sleep(10); - if (!isDone && s_Stopwatch.Elapsed.TotalSeconds > timeout) - throw new TimeoutException("Asset Store batch mode operation timed out."); - } while (!isDone); - - return isDone; - } - static string GetDraftAssetsPath(string localRootPath) - { - var chars = new[] {(char) 47}; - var fileName = localRootPath.Trim(chars).Replace('/', '_'); - return "Temp/uploadtool_" + fileName + ".unitypackage"; - } - static void Export(Package package, List localRootPaths, string toPath) - { - File.Delete(toPath); - - List paths = new List(); - - foreach (string localRootPath in localRootPaths) - { - var guids = GetGUIDS(package, localRootPath); - Debug.Log("[Asset Store Batch Mode] Number of assets to export: " + guids.Length); - - var sb = new StringBuilder(); - sb.AppendLine("[Asset Store Batch Mode] Exported asset list:"); - - // Note - implementation here differs from Asset Store tools, in order to work properly in batch mode - - foreach (string guid in guids) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - paths.Add(assetPath); - sb.AppendLine(assetPath); - } - - Debug.Log(sb.ToString()); - } - - AssetDatabase.ExportPackage(paths.ToArray(), toPath); - } - static bool IsValidProjectFolder(string directory) - { - return Application.dataPath.Length <= directory.Length && - directory.Substring(0, Application.dataPath.Length) == Application.dataPath && - Directory.Exists(directory); - } - - static string SetRootPath(Package package, string path) - { - var localRootPath = path.Substring(Application.dataPath.Length); - - if (localRootPath == string.Empty) - localRootPath = "/"; - - package.RootPath = path; - - return localRootPath; - } - - static string CheckContent(Package package, string localRootPath) - { - var errorMessage = string.Empty; - var disallowedFileTypes = new[] - { - ".mb", - ".ma", - ".max", - ".c4d", - ".blend", - ".3ds", - ".jas", - ".fbm", - ".dds", - ".pvr" - }; - foreach (var guid in GetGUIDS(package, localRootPath)) - { - var path = AssetDatabase.GUIDToAssetPath(guid); - foreach (var fileType in disallowedFileTypes) - { - if (path.EndsWith(fileType)) - { - if (errorMessage != string.Empty) - errorMessage += "\n"; - if (fileType == ".fbm") - errorMessage += "Disallowed file type:" + fileType + - " - disable embedded media when exporting to .fbx"; - else if (fileType == "jpg" || fileType == "jpeg") - Debug.LogWarning("[Asset Store Batch Mode] It is strongly encouraged to use PNG format instead of JPEG for " + path); - else - errorMessage += "Disallowed file type: " + errorMessage; - } - } - } - return errorMessage; - } - static string[] GetGUIDS(Package package, string localRootPath) - { - var includeProjectSettings = package.IsCompleteProjects && !s_SkipProjectSettings; - var str1 = "Assets" + (localRootPath ?? string.Empty); - var chars = new[] { (char)47 }; - var path1 = str1.Trim(chars); - var assetsItemArray = AssetServer.BuildExportPackageAssetListAssetsItems(AssetServer.CollectAllChildren(AssetDatabase.AssetPathToGUID(path1), new string[0]), true); - var list = new List(); - var str2 = path1.ToLower(); - foreach (var assetsItem in assetsItemArray) - { - var str3 = AssetDatabase.GUIDToAssetPath(assetsItem.guid).ToLower(); - if (str3.StartsWith("assets/plugins") || str3.Contains("standard assets") || str3.StartsWith(str2)) - list.Add(assetsItem.guid); - } - if (includeProjectSettings) - { - foreach (var path2 in Directory.GetFiles("ProjectSettings")) - { - var str3 = AssetDatabase.AssetPathToGUID(path2); - if (str3.Length > 0) - list.Add(str3); - } - } - var array = new string[list.Count]; - list.CopyTo(array); - return array; - } -} -#endregion - -#region Proxy classes -internal interface IReflectedType -{ - Type GetRuntimeType(); - object GetRuntimeObject(); - -} - -abstract class AssetStoreToolsReflectedType : IReflectedType -{ - Type m_RuntimeType; - object m_RuntimeObject; - - public Type GetRuntimeType() - { - return m_RuntimeType; - } - public object GetRuntimeObject() - { - return m_RuntimeObject; - } - protected void SetRuntimeType(Type value) - { - m_RuntimeType = value; - } - protected void SetRuntimeObject(object value) - { - m_RuntimeObject = value; - } - protected AssetStoreToolsReflectedType() {} - protected AssetStoreToolsReflectedType(bool createObject) - { - var typeName = GetType().Name; - var assembly = typeof (DebugUtils).Assembly; - - SetRuntimeType(assembly.GetType(typeName, true)); - - if (createObject) - { - SetRuntimeObject(Activator.CreateInstance(m_RuntimeType)); - } - } -} - -internal class AssetStorePublisher : AssetStoreToolsReflectedType -{ - public AssetStorePublisher() : base(true) { } -} - -internal class PackageDataSource : AssetStoreToolsReflectedType -{ - public PackageDataSource() : base(true) {} - - public IList GetAllPackages() - { - var packages = GetRuntimeObject().Invoke("GetAllPackages") as IList; - - if (packages == null) - { - throw new TargetException("GetAllPackages returned an invalid value"); - } - - var packageList = new List(); - - for (var i = 0; i < packages.Count; ++i) - { - packageList.Add(new Package(packages[i])); - } - - return packageList; - } -} - -internal class Package : AssetStoreToolsReflectedType -{ - public Package(object package) : base(false) - { - SetRuntimeObject(package); - } - public string Name - { - get { return GetRuntimeObject().GetFieldValue("Name"); } - } - public PublishedStatus Status - { - get { return (PublishedStatus)((int)GetRuntimeObject().GetPropertyValue("Status")); } - } - public bool IsCompleteProjects - { - get { return GetRuntimeObject().GetFieldValue("IsCompleteProjects"); } - } - public string RootPath - { - get { return GetRuntimeObject().GetFieldValue("RootPath"); } - set { GetRuntimeObject().SetFieldValue("RootPath", value); } - } - - internal enum PublishedStatus - { - Draft, - Disabled, - Published, - PendingReview, - } -} - -internal class AssetStoreClient : AssetStoreToolsReflectedType -{ - static AssetStoreClient s_Instance; - - static AssetStoreClient() - { - s_Instance = new AssetStoreClient(); - s_Instance.GetRuntimeType().Assembly.GetType("AssetStoreManager").SetFieldValue("sDbg", true); - } - - private AssetStoreClient() : base(false) { } - - public static bool LoggedIn() - { - return s_Instance.GetRuntimeType().Invoke("LoggedIn"); - } - public static void LoginWithCredentials(string username, string password, bool rememberMe, DoneLoginCallback callback) - { - Type doneLoginCallbackType; - var doneLoginCallback = CreateCallbackDelegate("DoneLoginCallback", callback, out doneLoginCallbackType); - s_Instance.GetRuntimeType().Invoke("LoginWithCredentials", username.Param(), password.Param(), rememberMe.Param(), new Parameter(doneLoginCallback, doneLoginCallbackType)); - } - public static void Update() - { - s_Instance.GetRuntimeType().Invoke("Update"); - } - public static void Logout() - { - s_Instance.GetRuntimeType().Invoke("Logout"); - } - - internal static Delegate CreateCallbackDelegate(string name, Delegate del, out Type type) - { - type = s_Instance.GetRuntimeType().GetNestedType(name); - return del == null ? null : Delegate.CreateDelegate(type, del.Method); - } - public delegate void DoneLoginCallback(string errorMessage); - public delegate void ProgressCallback(double pctUp, double pctDown); - public delegate void DoneCallback(string errorMessage); -} - -internal class AssetStoreAPI : AssetStoreToolsReflectedType -{ - static AssetStoreAPI s_Instance; - - static AssetStoreAPI() - { - s_Instance = new AssetStoreAPI(); - } - - private AssetStoreAPI() : base(false) { } - - public static void GetMetaData(AssetStorePublisher publisherAccount, PackageDataSource packageDataSource, DoneCallback callback) - { - Type doneCallbackType; - var doneCallback = CreateCallbackDelegate("DoneCallback", callback, out doneCallbackType); - s_Instance.GetRuntimeType().Invoke("GetMetaData", publisherAccount.GetRuntimeObject().Param(), packageDataSource.GetRuntimeObject().Param(), new Parameter(doneCallback, doneCallbackType)); - } - public static void UploadAssets(Package package, string localRootGuid, string localRootPath, string projectPath, string draftAssetsPath, DoneCallback callback, AssetStoreClient.ProgressCallback progressCallback) - { - Type doneCallbackType, clientProgressCallbackType; - var doneCallback = CreateCallbackDelegate("DoneCallback", callback, out doneCallbackType); - var clientProgressCallback = AssetStoreClient.CreateCallbackDelegate("ProgressCallback", progressCallback, out clientProgressCallbackType); - s_Instance.GetRuntimeType().Invoke("UploadAssets", package.GetRuntimeObject().Param(), localRootGuid.Param(), localRootPath.Param(), projectPath.Param(), draftAssetsPath.Param(), new Parameter(doneCallback, doneCallbackType), new Parameter(clientProgressCallback, clientProgressCallbackType)); - } - - internal static Delegate CreateCallbackDelegate(string name, Delegate del, out Type type) - { - type = s_Instance.GetRuntimeType().GetNestedType(name); - return del == null ? null : Delegate.CreateDelegate(type, del.Method); - } - public delegate void DoneCallback(string errorMessage); -} - -internal class AssetStorePackageController : AssetStoreToolsReflectedType -{ - static AssetStorePackageController s_Instance; - - static AssetStorePackageController() - { - s_Instance = new AssetStorePackageController(); - } - - private AssetStorePackageController() : base(false) { } - - public static string GetLocalRootGUID(Package package) - { - return s_Instance.GetRuntimeType().Invoke("GetLocalRootGUID", package.GetRuntimeObject().Param()); - } -} -internal class AssetServer : AssetStoreToolsReflectedType -{ - static AssetServer s_Instance; - - static AssetServer() - { - s_Instance = new AssetServer(); - } - - private AssetServer() - { - var assembly = typeof(DebugUtils).Assembly; - SetRuntimeType(assembly.GetType("UnityEditor.AssetServer", true)); - } - - public static string[] CollectAllChildren(string guid, string[] collection) - { - return s_Instance.GetRuntimeType().Invoke("CollectAllChildren", guid.Param(), collection.Param()); - } - - public static AssetsItem[] BuildExportPackageAssetListAssetsItems(string[] guids, bool dependencies) - { - return s_Instance.GetRuntimeType().Invoke("BuildExportPackageAssetListAssetsItems", guids.Param(), dependencies.Param()); - } - public static void ExportPackage(string[] guids, string path) - { - s_Instance.GetRuntimeType().Invoke("ExportPackage", guids.Param(), path.Param()); - } -} - -#endregion diff --git a/Assets/Editor/AssetStoreBatchMode/ReflectionHelpers.cs b/Assets/Editor/AssetStoreBatchMode/ReflectionHelpers.cs deleted file mode 100644 index cf27db92..00000000 --- a/Assets/Editor/AssetStoreBatchMode/ReflectionHelpers.cs +++ /dev/null @@ -1,376 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using UnityEditor; -using Object = UnityEngine.Object; - -namespace Thinksquirrel.Development.Integration -{ - struct Parameter - { - #region Public API - public object obj; - public Type parameterType; - public Parameter(object obj) - { - this.obj = obj; - parameterType = obj == null ? typeof(object) : obj.GetType(); - } - public Parameter(object obj, Type type) - { - this.obj = obj; - parameterType = type; - } - #endregion - } - - static class ReflectionHelpers - { - internal static T Cast(this object obj) - { - if (obj == null) throw new ArgumentNullException("obj"); - var type = typeof(T); - return (T)Cast(obj, type); - } - internal static object Cast(this object obj, Type type) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (type == null) throw new ArgumentNullException("type"); - if (type.IsEnum) - { - return Enum.ToObject(type, obj); - } - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - return Convert.ChangeType(obj, Nullable.GetUnderlyingType(type)); - } - return Convert.ChangeType(obj, type); - } - internal static Type GetEditorType(string typeName) - { - if (typeName == null) throw new ArgumentNullException("typeName"); - return GetTypeInAssembly(typeof(Editor), string.Format("UnityEditor.{0}", typeName)); - } - internal static FieldInfo GetField(this object obj, string fieldName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - - var t = obj.GetType(); - - while (t != null) - { - var candidate = t.GetField(fieldName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return candidate; - t = t.BaseType; - } - return null; - - } - internal static Type GetFieldType(this Type type, string fieldName) - { - if (type == null) throw new ArgumentNullException("type"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - - var t = type; - - while (t != null) - { - var candidate = t.GetField(fieldName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return candidate.FieldType; - t = t.BaseType; - } - return null; - } - internal static Type GetFieldType(this object obj, string fieldName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - return GetFieldType(obj.GetType(), fieldName); - } - internal static object GetFieldValue(this Type type, string fieldName) - { - if (type == null) throw new ArgumentNullException("type"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - return GetFieldValue(type, null, fieldName); - } - internal static T GetFieldValue(this Type type, string fieldName) - { - if (type == null) throw new ArgumentNullException("type"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - return GetFieldValue(type, null, fieldName); - } - internal static object GetFieldValue(this object obj, string fieldName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - return GetFieldValue(obj.GetType(), obj, fieldName); - } - internal static T GetFieldValue(this object obj, string fieldName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - return GetFieldValue(obj.GetType(), obj, fieldName); - } - internal static MethodInfo GetMethod(this object obj, string methodName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (methodName == null) throw new ArgumentNullException("methodName"); - - var t = obj.GetType(); - - while (t != null) - { - var candidate = t.GetMethod(methodName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return candidate; - t = t.BaseType; - } - - return null; - } - internal static PropertyInfo GetProperty(this object obj, string propertyName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - - var t = obj.GetType(); - - while (t != null) - { - var candidate = t.GetProperty(propertyName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return candidate; - t = t.BaseType; - } - return null; - } - internal static Type GetPropertyType(this Type type, string propertyName) - { - if (type == null) throw new ArgumentNullException("type"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - - var t = type; - - while (t != null) - { - var candidate = t.GetProperty(propertyName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return candidate.PropertyType; - t = t.BaseType; - } - return null; - } - internal static Type GetPropertyType(this object obj, string propertyName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - return GetPropertyType(obj.GetType(), propertyName); - } - internal static object GetPropertyValue(this Type type, string propertyName) - { - if (type == null) throw new ArgumentNullException("type"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - return GetPropertyValue(type, null, propertyName); - } - internal static T GetPropertyValue(this Type type, string propertyName) - { - if (type == null) throw new ArgumentNullException("type"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - return GetPropertyValue(type, null, propertyName); - } - internal static object GetPropertyValue(this object obj, string propertyName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - return GetPropertyValue(obj.GetType(), obj, propertyName); - } - internal static T GetPropertyValue(this object obj, string propertyName) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - return GetPropertyValue(obj.GetType(), obj, propertyName); - } - internal static Type GetRuntimeType(string typeName) - { - if (typeName == null) throw new ArgumentNullException("typeName"); - return GetTypeInAssembly(typeof(Object), string.Format("UnityEngine.{0}", typeName)); - } - internal static Type GetTypeInAssembly(this Type type, string typeName) - { - if (type == null) throw new ArgumentNullException("type"); - if (typeName == null) throw new ArgumentNullException("typeName"); - return type.Assembly.GetType(typeName); - } - internal static object Invoke(this Type type, string methodName, params Parameter[] parameters) - { - if (type == null) throw new ArgumentNullException("type"); - if (methodName == null) throw new ArgumentNullException("methodName"); - return Invoke(type, null, methodName, parameters); - } - internal static T Invoke(this Type type, string methodName, params Parameter[] parameters) - { - if (type == null) throw new ArgumentNullException("type"); - if (methodName == null) throw new ArgumentNullException("methodName"); - return Invoke(type, null, methodName, parameters); - } - internal static object Invoke(this object obj, string methodName, params Parameter[] parameters) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (methodName == null) throw new ArgumentNullException("methodName"); - return Invoke(obj.GetType(), obj, methodName, parameters); - } - internal static T Invoke(this object obj, string methodName, params Parameter[] parameters) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (methodName == null) throw new ArgumentNullException("methodName"); - return Invoke(obj.GetType(), obj, methodName, parameters); - } - internal static Type NestedType(this Type type, string typeName) - { - if (type == null) throw new ArgumentNullException("type"); - if (typeName == null) throw new ArgumentNullException("typeName"); - return type.GetNestedType(typeName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - } - internal static Parameter Param(this object obj) - { - if (obj == null) throw new ArgumentNullException("obj"); - return new Parameter(obj); - } - internal static void SetFieldValue(this Type type, string fieldName, object value) - { - if (type == null) throw new ArgumentNullException("type"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - SetFieldValue(type, null, fieldName, value); - } - internal static void SetFieldValue(this object obj, string fieldName, object value) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (fieldName == null) throw new ArgumentNullException("fieldName"); - SetFieldValue(obj.GetType(), obj, fieldName, value); - } - internal static void SetPropertyValue(this Type type, string propertyName, object value) - { - if (type == null) throw new ArgumentNullException("type"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - SetPropertyValue(type, null, propertyName, value); - } - internal static void SetPropertyValue(this object obj, string propertyName, object value) - { - if (obj == null) throw new ArgumentNullException("obj"); - if (propertyName == null) throw new ArgumentNullException("propertyName"); - SetPropertyValue(obj.GetType(), obj, propertyName, value); - } - internal static object Create(this Type type, params Parameter[] parameters) - { - return Create(type, parameters); - } - internal static T Create(this Type type, params Parameter[] parameters) - { - if (type == null) throw new ArgumentNullException("type"); - return - (T) - type.GetConstructor( - BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, - parameters.Select(p => p.parameterType).ToArray(), null) - .Invoke(parameters.Select(p => p.obj).ToArray()); - } - static T GetFieldValue(Type type, object obj, string fieldName) - { - var t = type; - - while (t != null) - { - var candidate = t.GetField(fieldName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return (T)candidate.GetValue(obj); - t = t.BaseType; - } - return default(T); - } - static T GetPropertyValue(Type type, object obj, string propertyName) - { - var t = type; - - while (t != null) - { - var candidate = t.GetProperty(propertyName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) return (T)candidate.GetValue(obj, null); - t = t.BaseType; - } - - return default(T); - } - static T Invoke(Type type, object obj, string methodName, params Parameter[] parameters) - { - var t = type; - - while (t != null) - { - var candidate = t.GetMethod(methodName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public, null, parameters.Select(p => p.parameterType).ToArray(), - null); - - if (candidate != null) return (T)candidate.Invoke(obj, parameters.Select(p => p.obj).ToArray()); - t = t.BaseType; - } - - return default(T); - } - static void SetFieldValue(Type type, object obj, string fieldName, object value) - { - var t = type; - - while (t != null) - { - var candidate = t.GetField(fieldName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - if (candidate != null) - { - candidate.SetValue(obj, value); - return; - } - t = t.BaseType; - } - } - static void SetPropertyValue(Type type, object obj, string propertyName, object value) - { - var t = type; - - while (t != null) - { - var candidate = t.GetProperty(propertyName, - BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public); - - if (candidate != null) - { - candidate.SetValue(obj, value, null); - return; - } - t = t.BaseType; - } - } - } -} diff --git a/Assets/Editor/AssetStoreBatchMode/lib.meta b/Assets/Editor/AssetStoreBatchMode/lib.meta deleted file mode 100644 index f5b9cf50..00000000 --- a/Assets/Editor/AssetStoreBatchMode/lib.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: a1c01a33da1f44be0a3c91cba9fa5884 -folderAsset: yes -timeCreated: 1490992909 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/AUTHORS b/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/AUTHORS deleted file mode 100644 index 3d85480a..00000000 --- a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Jonathan Pryor diff --git a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/AUTHORS.meta b/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/AUTHORS.meta deleted file mode 100644 index 3a9d7f94..00000000 --- a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/AUTHORS.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 38b42b9398b284af99ecb816a3beaff6 -timeCreated: 1490992910 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/COPYING b/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/COPYING deleted file mode 100644 index 955778ba..00000000 --- a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/COPYING +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2008 Novell (http://www.novell.com) - -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/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/COPYING.meta b/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/COPYING.meta deleted file mode 100644 index 37f01869..00000000 --- a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/COPYING.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 40910d5a93329431e9e894c0c082f4e8 -timeCreated: 1490992910 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/NDesk.Options.dll b/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/NDesk.Options.dll deleted file mode 100644 index df458789..00000000 Binary files a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/NDesk.Options.dll and /dev/null differ diff --git a/Assets/Editor/InternalMenuItems.cs b/Assets/Editor/InternalMenuItems.cs index f53f777b..a65050c0 100644 --- a/Assets/Editor/InternalMenuItems.cs +++ b/Assets/Editor/InternalMenuItems.cs @@ -6,25 +6,10 @@ namespace PatchKit.Unity.Editor public class InternalMenuItems { - [MenuItem("Tools/PatchKit Patcher Internal/Upload Asset Store Package", false, 1)] - public static void UploadAssetStorePackage() - { - if (string.IsNullOrEmpty(InternalSettings.Email) || string.IsNullOrEmpty(InternalSettings.Password)) - { - Debug.LogError("Please set email and password in internal settings"); - return; - } - - AssetStoreBatchMode.UploadAssetStorePackage( - InternalSettings.Email, InternalSettings.Password, - "PatchKit Patcher", - new [] {"PatchKit Patcher", "Plugins/PatchKit", "Plugins/UniRx", "StreamingAssets"}); - } - [MenuItem("Tools/PatchKit Patcher Internal/Settings", false, 100)] public static void Settings() { - InternalSettings.Show(); + InternalSettings.ShowSettings(); } } diff --git a/Assets/Editor/InternalSettings.cs b/Assets/Editor/InternalSettings.cs index 1c2bc925..1fe44de5 100644 --- a/Assets/Editor/InternalSettings.cs +++ b/Assets/Editor/InternalSettings.cs @@ -20,7 +20,7 @@ public static string Password set { EditorPrefs.SetString(PasswordKey, value); } } - public static void Show() + public static void ShowSettings() { EditorWindow window = GetWindow(); window.Show(); diff --git a/Assets/Editor/Tests/GZipStreamWrapperTest.cs b/Assets/Editor/Tests/GZipStreamWrapperTest.cs new file mode 100644 index 00000000..071165bf --- /dev/null +++ b/Assets/Editor/Tests/GZipStreamWrapperTest.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using System.Text; +using Ionic.Zlib; +using NUnit.Framework; +using PatchKit.Unity.Utilities; + +public class GZipStreamWrapperTest +{ + private class MockStream : Stream + { + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanSeek + { + get + { + return false; + } + } + + public override bool CanWrite + { + get + { + return false; + } + } + public override long Length + { + get + { + return _internalStream.Length; + } + } + + public override long Position + { + get + { + return _internalStream.Position; + } + + set + { + _internalStream.Position = value; + } + } + + private readonly int _packetSize; + + private readonly MemoryStream _internalStream; + + public MockStream(byte[] data, int packetSize) + { + _packetSize = packetSize; + _internalStream = new MemoryStream(data); + } + + public override void Flush() + { + throw new System.NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _internalStream.Read(buffer, offset, System.Math.Min(count, _packetSize)); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _internalStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _internalStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new System.NotImplementedException(); + } + } + + private const string RawData = "1234567890987654321qwerweqrfa"; + + private byte[] compressedData; + + [SetUp] + public void SetUp() + { + using (var targetStream = new MemoryStream()) + { + using (var sourceStream = new MemoryStream(Encoding.ASCII.GetBytes(RawData))) + { + using (var gzipStream = new GZipStream(targetStream, CompressionMode.Compress)) + { + Streams.Copy(sourceStream, gzipStream); + } + } + + compressedData = targetStream.ToArray(); + } + } + + [Test] + public void Raw_GZip_Stream_Fails_On_Mocked_Stream() + { + TestDelegate act = () => { + using (var targetStream = new MemoryStream()) + { + using (var mockStream = new MockStream(compressedData, 5)) + { + using (var gzipStream = new GZipStream(mockStream, CompressionMode.Decompress)) + { + Streams.Copy(gzipStream, targetStream); + } + } + } + }; + + Assert.Throws(typeof(ZlibException), act); + } + + [Test] + public void Wrapped_Read_Succeeds_On_Mocked_Stream() + { + string decompressed; + using (var targetStream = new MemoryStream()) + { + using (var mockStream = new MockStream(compressedData, 5)) + { + using (var wrapperStream = new GZipReadWrapperStream(mockStream)) + { + using (var gzipStream = new GZipStream(wrapperStream, CompressionMode.Decompress)) + { + Streams.Copy(gzipStream, targetStream); + } + } + } + + decompressed = Encoding.ASCII.GetString(targetStream.ToArray()); + } + + Assert.That(decompressed == RawData); + } +} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClient.cs.meta b/Assets/Editor/Tests/GZipStreamWrapperTest.cs.meta similarity index 76% rename from Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClient.cs.meta rename to Assets/Editor/Tests/GZipStreamWrapperTest.cs.meta index 8c080c3f..ef028785 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClient.cs.meta +++ b/Assets/Editor/Tests/GZipStreamWrapperTest.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: d27470050bf037c48b25977ef7f260bb -timeCreated: 1478719445 +guid: 94cf6b508f9924556b878590972c3182 +timeCreated: 1535632985 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/Assets/Editor/Tests/RemoteResourceDownloaderTest.cs b/Assets/Editor/Tests/RemoteResourceDownloaderTest.cs index d8b4b7dd..94851204 100644 --- a/Assets/Editor/Tests/RemoteResourceDownloaderTest.cs +++ b/Assets/Editor/Tests/RemoteResourceDownloaderTest.cs @@ -36,7 +36,6 @@ private RemoteResource CreateTestRemoteResource() ChunksData = CreateTestChunksData(), Size = 1, HashCode = "hashcode", - TorrentUrls = new[] {"torrent-url"}, ResourceUrls = new[] { // TODO: Test when MetaUrl is set @@ -50,7 +49,7 @@ private RemoteResource CreateTestRemoteResource() new ResourceUrl { Url = "url-2", - MetaUrl = null, + MetaUrl = null, Country = "PL", PartSize = 0, } @@ -89,66 +88,21 @@ public void TearDown() } [Test] - public void UseTorrentDownloaderFirst() - { - RemoteResource resource = CreateTestRemoteResource(); - - var httpDownloader = Substitute.For(); - var chunkedHttpDownloader = Substitute.For(); - var torrentDownloader = Substitute.For(); - - var downloader = new RemoteResourceDownloader(_filePath, _metaFilePath, resource, true, - (path, urls) => httpDownloader, - (path, urls, data, size) => chunkedHttpDownloader, - (path, filePath, bytes) => torrentDownloader); - - downloader.Download(CancellationToken.Empty); - - chunkedHttpDownloader.DidNotReceiveWithAnyArgs().Download(CancellationToken.Empty); - torrentDownloader.ReceivedWithAnyArgs().Download(CancellationToken.Empty); - } - - [Test] - public void UseChunkedHttpDownloaderIfTorrentFails() - { - RemoteResource resource = CreateTestRemoteResource(); - - var httpDownloader = Substitute.For(); - var chunkedHttpDownloader = Substitute.For(); - var torrentDownloader = Substitute.For(); - torrentDownloader.When(t => t.Download(CancellationToken.Empty)).Do( - info => { throw new DownloadFailureException("Test."); }); - - var downloader = new RemoteResourceDownloader(_filePath, _metaFilePath, resource, true, - (path, urls) => httpDownloader, - (path, urls, data, size) => chunkedHttpDownloader, - (path, filePath, bytes) => torrentDownloader); - - downloader.Download(CancellationToken.Empty); - - chunkedHttpDownloader.ReceivedWithAnyArgs().Download(CancellationToken.Empty); - torrentDownloader.ReceivedWithAnyArgs().Download(CancellationToken.Empty); - } - - [Test] - public void UseChunkedHttpDownloaderIfTorrentIsNotUsed() + public void UseChunkedHttpDownloader() { RemoteResource resource = CreateTestRemoteResource(); var httpDownloader = Substitute.For(); var chunkedHttpDownloader = Substitute.For(); - var torrentDownloader = Substitute.For(); - var downloader = new RemoteResourceDownloader(_filePath, _metaFilePath, resource, false, + var downloader = new RemoteResourceDownloader(_filePath, _metaFilePath, resource, (path, urls) => httpDownloader, - (path, urls, data, size) => chunkedHttpDownloader, - (path, filePath, bytes) => torrentDownloader); + (path, urls, data, size) => chunkedHttpDownloader); downloader.Download(CancellationToken.Empty); httpDownloader.DidNotReceiveWithAnyArgs().Download(CancellationToken.Empty); chunkedHttpDownloader.ReceivedWithAnyArgs().Download(CancellationToken.Empty); - torrentDownloader.DidNotReceiveWithAnyArgs().Download(CancellationToken.Empty); } [Test] @@ -159,17 +113,14 @@ public void UseHttpDownloaderIfChunksAreNotAvailable() var httpDownloader = Substitute.For(); var chunkedHttpDownloader = Substitute.For(); - var torrentDownloader = Substitute.For(); - var downloader = new RemoteResourceDownloader(_filePath, _metaFilePath, resource, false, + var downloader = new RemoteResourceDownloader(_filePath, _metaFilePath, resource, (path, urls) => httpDownloader, - (path, urls, data, size) => chunkedHttpDownloader, - (path, filePath, bytes) => torrentDownloader); + (path, urls, data, size) => chunkedHttpDownloader); downloader.Download(CancellationToken.Empty); httpDownloader.ReceivedWithAnyArgs().Download(CancellationToken.Empty); chunkedHttpDownloader.DidNotReceiveWithAnyArgs().Download(CancellationToken.Empty); - torrentDownloader.DidNotReceiveWithAnyArgs().Download(CancellationToken.Empty); } } \ No newline at end of file diff --git a/Assets/Editor/AssetStoreBatchMode.meta b/Assets/PatchKit Patcher/Editor/Building.meta similarity index 67% rename from Assets/Editor/AssetStoreBatchMode.meta rename to Assets/PatchKit Patcher/Editor/Building.meta index 245d666c..13b533e5 100644 --- a/Assets/Editor/AssetStoreBatchMode.meta +++ b/Assets/PatchKit Patcher/Editor/Building.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 -guid: 9f9a76fb9653843d6b4b7b89f4e6aa65 +guid: 494532905226742bb8e32fc214adc0e5 folderAsset: yes -timeCreated: 1490992909 +timeCreated: 1530872824 licenseType: Free DefaultImporter: userData: diff --git a/Assets/PatchKit Patcher/Editor/CustomBuilding.cs b/Assets/PatchKit Patcher/Editor/Building/CustomBuilding.cs similarity index 99% rename from Assets/PatchKit Patcher/Editor/CustomBuilding.cs rename to Assets/PatchKit Patcher/Editor/Building/CustomBuilding.cs index a086790e..ed1c35c5 100644 --- a/Assets/PatchKit Patcher/Editor/CustomBuilding.cs +++ b/Assets/PatchKit Patcher/Editor/Building/CustomBuilding.cs @@ -7,7 +7,7 @@ namespace PatchKit.Unity { - public class CustomBuildScripts + public class CustomBuilding { [MenuItem("Tools/Build/Windows x86")] public static void BuildWindows86 () diff --git a/Assets/PatchKit Patcher/Editor/CustomBuilding.cs.meta b/Assets/PatchKit Patcher/Editor/Building/CustomBuilding.cs.meta similarity index 76% rename from Assets/PatchKit Patcher/Editor/CustomBuilding.cs.meta rename to Assets/PatchKit Patcher/Editor/Building/CustomBuilding.cs.meta index a09d3f00..0868fe7e 100644 --- a/Assets/PatchKit Patcher/Editor/CustomBuilding.cs.meta +++ b/Assets/PatchKit Patcher/Editor/Building/CustomBuilding.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 1f53eae719d30884788d43703df00a84 -timeCreated: 1519824255 +guid: 4b2dcc12d347b49efa5d957c04b60533 +timeCreated: 1530872843 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/Assets/PatchKit Patcher/Editor/Building/PostBuild.cs b/Assets/PatchKit Patcher/Editor/Building/PostBuild.cs new file mode 100644 index 00000000..b7e5fdc8 --- /dev/null +++ b/Assets/PatchKit Patcher/Editor/Building/PostBuild.cs @@ -0,0 +1,29 @@ +using System.CodeDom; +using System.Linq; +using JetBrains.Annotations; +using UnityEditor; +using UnityEditor.Callbacks; + +namespace PatchKit.Unity +{ + public class PostBuild + { + private static readonly BuildTarget[] ValidBuildTargets = { + BuildTarget.StandaloneLinux, + BuildTarget.StandaloneLinux64, + BuildTarget.StandaloneWindows, + BuildTarget.StandaloneWindows64, + BuildTarget.StandaloneOSXIntel64, + }; + + [PostProcessBuild, UsedImplicitly] + private static void Execute(BuildTarget buildTarget, string buildPath) + { + if (!ValidBuildTargets.Contains(buildTarget)) + { + string archString = buildTarget.ToString(); + EditorUtility.DisplayDialog("Warning", string.Format("PatchKit Patcher doesn't officially support {0} architecture. Errors may occur.", archString), "Ok"); + } + } + } +} \ No newline at end of file diff --git a/Assets/Editor/AssetStoreBatchMode/ReflectionHelpers.cs.meta b/Assets/PatchKit Patcher/Editor/Building/PostBuild.cs.meta similarity index 76% rename from Assets/Editor/AssetStoreBatchMode/ReflectionHelpers.cs.meta rename to Assets/PatchKit Patcher/Editor/Building/PostBuild.cs.meta index fb5ca85c..ce144de4 100644 --- a/Assets/Editor/AssetStoreBatchMode/ReflectionHelpers.cs.meta +++ b/Assets/PatchKit Patcher/Editor/Building/PostBuild.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 5abdc723f14704ea8928c32266a0e4ac -timeCreated: 1490992930 +guid: 2bde2528ec27f4f8e95d7bfb3ecdd2b0 +timeCreated: 1530872842 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/Assets/PatchKit Patcher/Editor/Manifest.cs b/Assets/PatchKit Patcher/Editor/Manifest.cs index 4f4a5554..fad1dfdf 100644 --- a/Assets/PatchKit Patcher/Editor/Manifest.cs +++ b/Assets/PatchKit Patcher/Editor/Manifest.cs @@ -23,6 +23,9 @@ public struct Argument public string Target; [JsonProperty(PropertyName = "target_arguments")] - public Argument[] Arguments; + public Argument[] Arguments; + + [JsonProperty(PropertyName = "capabilities")] + public string[] Capabilities; } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Editor/PatcherManifestCreator.cs b/Assets/PatchKit Patcher/Editor/PatcherManifestCreator.cs index ce3fb126..374f0ce8 100644 --- a/Assets/PatchKit Patcher/Editor/PatcherManifestCreator.cs +++ b/Assets/PatchKit Patcher/Editor/PatcherManifestCreator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using JetBrains.Annotations; using Newtonsoft.Json; @@ -9,13 +9,38 @@ namespace PatchKit.Unity.Editor { public static class PatcherManifestCreator { - private const int ManifestVersion = 2; + private const int ManifestVersion = 4; + + private static void SaveTestManifest(Manifest manifest) + { + string targetLocation = EditorUtility.SaveFilePanel("Choose test manifest location", "", "patcher.manifest", "test"); + + File.WriteAllText(targetLocation, JsonConvert.SerializeObject(manifest, Formatting.Indented)); + } + + [MenuItem("Tools/PatchKit Patcher Internal/Manifest/Windows")] + private static void CreateTestManifestWindows() + { + SaveTestManifest(WindowsManifest("BUILD_PATH")); + } + + [MenuItem("Tools/PatchKit Patcher Internal/Manifest/Linux")] + private static void CreateTestManifestLinux() + { + SaveTestManifest(LinuxManifest("BUILD_PATH")); + } + + [MenuItem("Tools/PatchKit Patcher Internal/Manifest/Osx")] + private static void CreateTestManifestOsx() + { + SaveTestManifest(OsxManifest("BUILD_PATH")); + } [PostProcessBuild, UsedImplicitly] private static void PostProcessBuild(BuildTarget buildTarget, string buildPath) { Manifest manifest; - + switch (buildTarget) { case BuildTarget.StandaloneWindows: @@ -40,6 +65,21 @@ private static void PostProcessBuild(BuildTarget buildTarget, string buildPath) File.WriteAllText(manifestPath, manifestContent); } + private static Manifest.Argument CreateManifestAgument(params string[] args) + { + return new Manifest.Argument { Value = args }; + } + + private static string[] Capabilities() + { + return new []{ + "pack1_compression_lzma2", + "security_fix_944", + "preselected_executable", + "execution_arguments" + }; + } + private static Manifest LinuxManifest(string buildPath) { string patcherExe = Path.GetFileName(buildPath); @@ -49,23 +89,19 @@ private static Manifest LinuxManifest(string buildPath) return new Manifest { ExeFileName = "sh", - ExeArguments = "\"" + launchScriptPath + "\" \"{exedir}\" \"" + patcherExe + "\" \"{secret}\" \"{installdir}\"", + ExeArguments = "\"" + launchScriptPath + "\" \"--exedir={exedir}\" --patcher-exe=\"" + patcherExe + "\" \"--secret={secret}\" \"--installdir={installdir}\"", Version = ManifestVersion, Target = "sh", + Capabilities = Capabilities(), Arguments = new Manifest.Argument[] { - new Manifest.Argument { Value = new string[] { - launchScriptPath - }}, - new Manifest.Argument { Value = new string[] { - "{exedir}", - patcherExe, - "{secret}", - "{installdir}" - }}, - new Manifest.Argument { Value = new string[] { - "{lockfile}" - }} + CreateManifestAgument(launchScriptPath), + CreateManifestAgument("--exedir={exedir}"), + CreateManifestAgument("--secret={secret}"), + CreateManifestAgument("--installdir={installdir}"), + CreateManifestAgument("--network-status={network-status}"), + CreateManifestAgument("--patcher-exe=" + patcherExe), + CreateManifestAgument("--lockfile={lockfile}"), } }; } @@ -79,16 +115,12 @@ private static Manifest WindowsManifest(string buildPath) Version = ManifestVersion, Target = "{exedir}/" + targetFile, + Capabilities = Capabilities(), Arguments = new Manifest.Argument[] { - new Manifest.Argument { Value = new string[] { - "--installdir", "{installdir}" - }}, - new Manifest.Argument { Value = new string[] { - "--lockfile", "{lockfile}" - }}, - new Manifest.Argument { Value = new string[] { - "--secret", "{secret}" - }}, + CreateManifestAgument("--installdir", "{installdir}"), + CreateManifestAgument("--lockfile", "{lockfile}"), + CreateManifestAgument("--secret", "{secret}"), + CreateManifestAgument("--{network-status}"), } }; } @@ -102,24 +134,16 @@ private static Manifest OsxManifest(string buildPath) Version = ManifestVersion, Target = "open", + Capabilities = Capabilities(), Arguments = new Manifest.Argument[] { - new Manifest.Argument { Value = new string[] { - "{exedir}/" + targetFile - }}, - new Manifest.Argument { Value = new string[] { - "--args" - }}, - new Manifest.Argument { Value = new string[] { - "--installdir", "{installdir}" - }}, - new Manifest.Argument { Value = new string[] { - "--lockfile", "{lockfile}" - }}, - new Manifest.Argument { Value = new string[] { - "--secret", "{secret}" - }}, + CreateManifestAgument("{exedir}/" + targetFile), + CreateManifestAgument("--args"), + CreateManifestAgument("--installdir", "{installdir}"), + CreateManifestAgument("--lockfile", "{lockfile}"), + CreateManifestAgument("--secret", "{secret}"), + CreateManifestAgument("--{network-status}"), } }; } } -} \ No newline at end of file +} diff --git a/Assets/PatchKit Patcher/Editor/ScreenSizeCorrection.cs b/Assets/PatchKit Patcher/Editor/ScreenSizeCorrection.cs index 52f65c84..48049f3a 100644 --- a/Assets/PatchKit Patcher/Editor/ScreenSizeCorrection.cs +++ b/Assets/PatchKit Patcher/Editor/ScreenSizeCorrection.cs @@ -12,7 +12,7 @@ public static class ScreenSizeCorrection public static void SaveScreenSize(BuildTarget buildTarget, string buildPath) { string content = string.Format("{0} {1}", PlayerSettings.defaultScreenWidth, PlayerSettings.defaultScreenHeight); - string filename = Path.Combine(CustomBuildScripts.PatcherDataDirectory(buildTarget, buildPath), BorderlessWindow.ScreenSizeFilename); + string filename = Path.Combine(CustomBuilding.PatcherDataDirectory(buildTarget, buildPath), BorderlessWindow.ScreenSizeFilename); File.WriteAllText(filename, content); } diff --git a/Assets/PatchKit Patcher/Editor/patcher.template.sh b/Assets/PatchKit Patcher/Editor/patcher.template.sh index 89995a7c..f9fe0d95 100755 --- a/Assets/PatchKit Patcher/Editor/patcher.template.sh +++ b/Assets/PatchKit Patcher/Editor/patcher.template.sh @@ -1,19 +1,70 @@ #!/usr/bin/env sh -EXEDIR=$1 -PATCHER_EXE=$2 -SECRET=$3 -INSTALLDIR=$4 -LOCKFILE=$5 +while [ "$1" != "" ]; do + PARAM=`echo $1 | awk -F= '{print $1}'` + VALUE=`echo $1 | awk -F= '{print $2}'` + case $PARAM in + --exedir) + EXEDIR=$VALUE + ;; + --patcher-exe) + PATCHER_EXE=$VALUE + ;; + --secret) + SECRET=$VALUE + ;; + --installdir) + INSTALLDIR=$VALUE + ;; + --lockfile) + LOCKFILE=$VALUE + ;; + --network-status) + NETWORK_STATUS=$VALUE + ;; + esac + shift +done + +if [ -z $PATCHER_EXE ] +then + echo "Missing --patcher-exe argument" + exit 1 +fi + +if [ -z $EXEDIR ] +then + echo "Missing --exedir argument" + exit 1 +fi + +if [ -z $SECRET ] +then + echo "Missing --secret argument" + exit 1 +fi + +if [ -z $INSTALLDIR ] +then + echo "Missing --installdir argument" + exit 1 +fi LD_DIRS="`find "$EXEDIR" -name "x86_64" -printf "%p:"`" LD_DIRS="$LD_DIRS`find "$EXEDIR" -name "x86" -printf "%p:"`" export LD_LIBRARY_PATH="$LD_DIRS" -if [ -n "$LOCKFILE" ] +ARGS="--installdir $INSTALLDIR --secret $SECRET" + +if [ ! -z "$LOCKFILE" ] then - "$EXEDIR/$PATCHER_EXE" --installdir "$INSTALLDIR" --secret "$SECRET" --lockfile "$LOCKFILE" -else - "$EXEDIR/$PATCHER_EXE" --installdir "$INSTALLDIR" --secret "$SECRET" + ARGS="$ARGS --lockfile $LOCKFILE" fi + +if [ ! -z "$NETWORK_STATUS" ] +then + ARGS="$ARGS --${NETWORK_STATUS}" +fi + +"$EXEDIR/$PATCHER_EXE" $ARGS \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Library/PatchKit.Api.dll b/Assets/PatchKit Patcher/Library/PatchKit.Api.dll index a58c31de..eff81a12 100644 Binary files a/Assets/PatchKit Patcher/Library/PatchKit.Api.dll and b/Assets/PatchKit Patcher/Library/PatchKit.Api.dll differ diff --git a/Assets/PatchKit Patcher/Library/PatchKit.Network.dll b/Assets/PatchKit Patcher/Library/PatchKit.Network.dll index ab41f4cb..4b21294a 100755 Binary files a/Assets/PatchKit Patcher/Library/PatchKit.Network.dll and b/Assets/PatchKit Patcher/Library/PatchKit.Network.dll differ diff --git a/Assets/PatchKit Patcher/Scripts/App.cs b/Assets/PatchKit Patcher/Scripts/App.cs index 896fb798..2c129909 100644 --- a/Assets/PatchKit Patcher/Scripts/App.cs +++ b/Assets/PatchKit Patcher/Scripts/App.cs @@ -29,8 +29,6 @@ public class App public readonly IRemoteMetaData RemoteMetaData; - private readonly string _appDataPath; - private readonly int _overrideLatestVersionId; public enum InstallStatus @@ -87,7 +85,6 @@ public App([NotNull] string appDataPath, [NotNull] ILocalDirectory localDirector throw new ArgumentNullException("remoteMetaData"); } - _appDataPath = appDataPath; LocalDirectory = localDirectory; LocalMetaData = localMetaData; DownloadDirectory = downloadDirectory; diff --git a/Assets/PatchKit Patcher/Scripts/AppData/FileSystem.meta b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem.meta new file mode 100644 index 00000000..9d6ed56e --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a8b1890ceba640069aa278569370878f +timeCreated: 1530878414 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/DirectoryOperations.cs b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/DirectoryOperations.cs similarity index 68% rename from Assets/PatchKit Patcher/Scripts/AppData/DirectoryOperations.cs rename to Assets/PatchKit Patcher/Scripts/AppData/FileSystem/DirectoryOperations.cs index ec2de59e..8e315e2a 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/DirectoryOperations.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/DirectoryOperations.cs @@ -1,8 +1,10 @@ using System; using System.IO; +using PatchKit.Network; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; -namespace PatchKit.Unity.Patcher.AppData +namespace PatchKit.Unity.Patcher.AppData.FileSystem { // ReSharper disable once InconsistentNaming public static class DirectoryOperations @@ -21,8 +23,15 @@ public static class DirectoryOperations /// Unauthorized access. public static bool IsDirectoryEmpty(string dirPath) { - Checks.ArgumentNotNullOrEmpty(dirPath, "dirPath"); - Checks.DirectoryExists(dirPath); + if (string.IsNullOrEmpty(dirPath)) + { + throw new ArgumentException("Value cannot be null or empty", "dirPath"); + } + + if (!Directory.Exists(dirPath)) + { + throw new ArgumentException("Directory must exist", "dirPath"); + } try { @@ -40,7 +49,7 @@ public static bool IsDirectoryEmpty(string dirPath) DebugLogger.LogError("Error while checking whether directory is empty: an exception occured. Rethrowing exception."); throw; } - + } /// @@ -49,10 +58,18 @@ public static bool IsDirectoryEmpty(string dirPath) /// The path. /// is null or empty. /// Unauthorized access. - public static void CreateParentDirectory(string path) + public static void CreateParentDirectory(string path, CancellationToken cancellationToken) { - Checks.ArgumentNotNullOrEmpty(path, "path"); + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentException("Value cannot be null or empty", "path"); + } + + RetryStrategy.TryExecute(() => CreateParentDirectoryInternal(path, cancellationToken), cancellationToken); + } + private static void CreateParentDirectoryInternal(string path, CancellationToken cancellationToken) + { try { DebugLogger.Log(string.Format("Creating parent directory for <{0}>.", path)); @@ -61,7 +78,7 @@ public static void CreateParentDirectory(string path) if (!string.IsNullOrEmpty(dirPath)) { - CreateDirectory(dirPath); + CreateDirectory(dirPath, cancellationToken); } DebugLogger.Log("Parent directory created."); @@ -79,10 +96,18 @@ public static void CreateParentDirectory(string path) /// The directory path. /// is null or empty. /// Unauthorized access. - public static void CreateDirectory(string dirPath) + public static void CreateDirectory(string dirPath, CancellationToken cancellationToken) { - Checks.ArgumentNotNullOrEmpty(dirPath, "dirPath"); + if (string.IsNullOrEmpty(dirPath)) + { + throw new ArgumentException("Value cannot be null or empty", "dirPath"); + } + + RetryStrategy.TryExecute(() => CreateDirectoryInternal(dirPath), cancellationToken); + } + private static void CreateDirectoryInternal(string dirPath) + { try { DebugLogger.Log(string.Format("Creating directory <{0}>.", dirPath)); @@ -106,11 +131,23 @@ public static void CreateDirectory(string dirPath) /// is null or empty. /// doesn't exist. /// Unauthorized access. - public static void Delete(string dirPath, bool recursive) + public static void Delete(string dirPath, CancellationToken cancellationToken, bool recursive = false) { - Checks.ArgumentNotNullOrEmpty(dirPath, "dirPath"); - Checks.DirectoryExists(dirPath); + if (string.IsNullOrEmpty(dirPath)) + { + throw new ArgumentException("Value cannot be null or empty", "dirPath)"); + } + if (!Directory.Exists(dirPath)) + { + throw new ArgumentException("Directory must exist", "dirPath"); + } + + RetryStrategy.TryExecute(() => DeleteInternal(dirPath, recursive), cancellationToken); + } + + private static void DeleteInternal(string dirPath, bool recursive) + { try { DebugLogger.Log(string.Format("Deleting directory {0}<{1}>.", diff --git a/Assets/PatchKit Patcher/Scripts/AppData/DirectoryOperations.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/DirectoryOperations.cs.meta similarity index 100% rename from Assets/PatchKit Patcher/Scripts/AppData/DirectoryOperations.cs.meta rename to Assets/PatchKit Patcher/Scripts/AppData/FileSystem/DirectoryOperations.cs.meta diff --git a/Assets/PatchKit Patcher/Scripts/AppData/FileOperations.cs b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/FileOperations.cs similarity index 59% rename from Assets/PatchKit Patcher/Scripts/AppData/FileOperations.cs rename to Assets/PatchKit Patcher/Scripts/AppData/FileSystem/FileOperations.cs index dd88f8d8..fcdff83f 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/FileOperations.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/FileOperations.cs @@ -1,8 +1,9 @@ using System; using System.IO; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; -namespace PatchKit.Unity.Patcher.AppData +namespace PatchKit.Unity.Patcher.AppData.FileSystem { // ReSharper disable once InconsistentNaming public static class FileOperations @@ -15,18 +16,39 @@ public static class FileOperations /// The source file path. /// The destination file path. /// if set to true and destination file exists then it is overwritten. + /// /// is null or empty. /// is null or empty. /// doesn't exist. /// parent directory doesn't exist. /// Unauthorized access. - public static void Copy(string sourceFilePath, string destinationFilePath, bool overwrite) + public static void Copy(string sourceFilePath, string destinationFilePath, bool overwrite, CancellationToken cancellationToken) { - Checks.ArgumentNotNullOrEmpty(sourceFilePath, "sourceFilePath"); - Checks.ArgumentNotNullOrEmpty(destinationFilePath, "destinationFilePath"); - Checks.FileExists(sourceFilePath); - Checks.ParentDirectoryExists(destinationFilePath); + if (string.IsNullOrEmpty(sourceFilePath)) + { + throw new ArgumentException("Value cannot be null or empty", "sourceFilePath"); + } + + if (string.IsNullOrEmpty(destinationFilePath)) + { + throw new ArgumentException("Value cannot be null or empty", "destinationFilePath"); + } + + if (!File.Exists(sourceFilePath)) + { + throw new ArgumentException("Source file must exist", "sourceFilePath"); + } + + if (!Directory.Exists(Path.GetDirectoryName(destinationFilePath))) + { + throw new ArgumentException("Parent directory of destination must exist", "destinationFilePath"); + } + RetryStrategy.TryExecute(() => CopyInternal(sourceFilePath, destinationFilePath, overwrite), cancellationToken); + } + + private static void CopyInternal(string sourceFilePath, string destinationFilePath, bool overwrite) + { try { DebugLogger.Log(string.Format("Copying file from <{0}> to <{1}> {2}...", @@ -49,14 +71,27 @@ public static void Copy(string sourceFilePath, string destinationFilePath, bool /// Deletes file. /// /// The file path. + /// /// is null or empty. /// doesn't exist. /// Unauthorized access. - public static void Delete(string filePath) + public static void Delete(string filePath, CancellationToken cancellationToken) { - Checks.ArgumentNotNullOrEmpty(filePath, "filePath"); - Checks.FileExists(filePath); + if (string.IsNullOrEmpty(filePath)) + { + throw new ArgumentException("Value cannot be null or empty", "filePath"); + } + + if (!File.Exists(filePath)) + { + throw new ArgumentException("File must exist", "filePath"); + } + + RetryStrategy.TryExecute(() => DeleteInternal(filePath), cancellationToken); + } + private static void DeleteInternal(string filePath) + { try { DebugLogger.Log(string.Format("Deleting file <{0}>.", filePath)); @@ -77,18 +112,39 @@ public static void Delete(string filePath) /// /// The source file path. /// The destination file path. + /// /// is null or empty. /// is null or empty. /// doesn't exist. /// parent directory doesn't exist. /// Unauthorized access. - public static void Move(string sourceFilePath, string destinationFilePath) + public static void Move(string sourceFilePath, string destinationFilePath, CancellationToken cancellationToken) { - Checks.ArgumentNotNullOrEmpty(sourceFilePath, "sourceFilePath"); - Checks.ArgumentNotNullOrEmpty(destinationFilePath, "destinationFilePath"); - Checks.FileExists(sourceFilePath); - Checks.ParentDirectoryExists(destinationFilePath); + if (string.IsNullOrEmpty(sourceFilePath)) + { + throw new ArgumentException("Value cannot be null or empty", "sourceFilePath"); + } + + if (string.IsNullOrEmpty(destinationFilePath)) + { + throw new ArgumentException("Value cannot be null or empty", "destinationFilePath"); + } + if (!File.Exists(sourceFilePath)) + { + throw new ArgumentException("Source file must exist", "sourceFilePath"); + } + + if (!Directory.Exists(Path.GetDirectoryName(destinationFilePath))) + { + throw new ArgumentException("Parent directory of destination must exist", "destinationFilePath"); + } + + RetryStrategy.TryExecute(() => MoveInternal(sourceFilePath, destinationFilePath), cancellationToken); + } + + private static void MoveInternal(string sourceFilePath, string destinationFilePath) + { try { DebugLogger.Log(string.Format("Moving file from <{0}> to <{1}>.", sourceFilePath, destinationFilePath)); diff --git a/Assets/PatchKit Patcher/Scripts/AppData/FileOperations.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/FileOperations.cs.meta similarity index 100% rename from Assets/PatchKit Patcher/Scripts/AppData/FileOperations.cs.meta rename to Assets/PatchKit Patcher/Scripts/AppData/FileSystem/FileOperations.cs.meta diff --git a/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/RetryStrategy.cs b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/RetryStrategy.cs new file mode 100644 index 00000000..9190aa3e --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/RetryStrategy.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; +using System.Threading; +using PatchKit.Network; +using PatchKit.Unity.Patcher.Cancellation; +using PatchKit.Unity.Patcher.Debug; +using PatchKit.Unity.Utilities; + +namespace PatchKit.Unity.Patcher.AppData.FileSystem +{ + public class RetryStrategy : IRequestRetryStrategy + { + private static readonly DebugLogger DebugLogger = new DebugLogger(typeof(RetryStrategy)); + + public const int DefaultTryCount = 10; + public const int DefaultDelayMsec = 500; + + private readonly int _tryCount; + private int _currentTry = 0; + private readonly int _delay; + + private RetryStrategy() + : this(DefaultTryCount, DefaultDelayMsec) + { + } + + public RetryStrategy(int tryCount, int delayBetweenEachTryMsec) + { + _tryCount = tryCount; + _delay = delayBetweenEachTryMsec; + } + + public void OnRequestSuccess() + { + // Do nothing + } + + public void OnRequestFailure() + { + _currentTry++; + } + + public int DelayBeforeNextTry { + get + { + return _delay; + } + } + + public bool ShouldRetry + { + get + { + return _currentTry <= _tryCount; + } + } + + public static void TryExecute(Action action, IRequestRetryStrategy retryStrategy, CancellationToken cancellationToken) + { + do + { + try + { + action(); + return; + } + catch (IOException e) + { + retryStrategy.OnRequestFailure(); + + if (!retryStrategy.ShouldRetry) + { + DebugLogger.LogError(string.Format("An IO Exception has occured: {0}. rethrowing.", e)); + throw; + } + + DebugLogger.LogWarning(string.Format("An IO Exception has occured: {0}. retrying...", e)); + Threading.CancelableSleep(retryStrategy.DelayBeforeNextTry, cancellationToken); + } + } while (retryStrategy.ShouldRetry); + } + + public static void TryExecute(Action action, CancellationToken cancellationToken) + { + TryExecute(action, new RetryStrategy(), cancellationToken); + } + } +} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/RetryStrategy.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/RetryStrategy.cs.meta new file mode 100644 index 00000000..65681c59 --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/AppData/FileSystem/RetryStrategy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 64d3bc8be69f475fa476f5f88544473a +timeCreated: 1530878815 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/BaseWritableDirectory.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/BaseWritableDirectory.cs index 79a82e60..d772202c 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/BaseWritableDirectory.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/BaseWritableDirectory.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; namespace PatchKit.Unity.Patcher.AppData.Local @@ -53,7 +55,7 @@ public virtual void PrepareForWriting() { DebugLogger.Log("Creating directory."); - DirectoryOperations.CreateDirectory(_path); + DirectoryOperations.CreateDirectory(_path, CancellationToken.Empty); _hasWriteAccess = true; } diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/DownloadDirectory.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/DownloadDirectory.cs index 2fc7dd3d..48a98a4b 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/DownloadDirectory.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/DownloadDirectory.cs @@ -1,4 +1,6 @@ using System.IO; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; namespace PatchKit.Unity.Patcher.AppData.Local @@ -46,8 +48,8 @@ public void Clear() if (Directory.Exists(Path)) { - DirectoryOperations.Delete(Path, true); - DirectoryOperations.CreateDirectory(Path); + DirectoryOperations.Delete(Path, CancellationToken.Empty, true); + DirectoryOperations.CreateDirectory(Path, CancellationToken.Empty); } } } diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/LimitedStream.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/LimitedStream.cs index 245fb621..920dc647 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/LimitedStream.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/LimitedStream.cs @@ -9,12 +9,31 @@ namespace PatchKit.Unity.Patcher.AppData.Local public class LimitedStream : Stream { private readonly Stream _orig; + private readonly long _bytesLimit; + private long _bytesLeft; + public long BytesLeft + { + get + { + return _bytesLeft; + } + } + + public long Limit + { + get + { + return _bytesLimit; + } + } + public LimitedStream(Stream orig, long bytesLimit) { _orig = orig; - _bytesLeft = bytesLimit; + _bytesLimit = bytesLimit; + _bytesLeft = _bytesLimit; } public override void Flush() diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/LocalMetaData.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/LocalMetaData.cs index 21e96ea8..61fb21a5 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/LocalMetaData.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/LocalMetaData.cs @@ -6,6 +6,8 @@ using JetBrains.Annotations; using Newtonsoft.Json; using PatchKit.Logging; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; namespace PatchKit.Unity.Patcher.AppData.Local @@ -177,7 +179,7 @@ private void CreateDataDir() string dirPath = Path.GetDirectoryName(_filePath); if (dirPath != null) { - Directory.CreateDirectory(dirPath); + DirectoryOperations.CreateDirectory(dirPath, CancellationToken.Empty); } } @@ -205,7 +207,7 @@ private void LoadData() { _logger.LogDebug("Deprecated data file exists. Moving it to a new location..."); CreateDataDir(); - File.Move(_deprecatedFilePath, _filePath); + FileOperations.Move(_deprecatedFilePath, _filePath, CancellationToken.Empty); _logger.LogDebug("Deprecated data file moved."); if (TryLoadDataFromFile()) diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Meta.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Meta.cs index b8dd92b4..f0dc4e8e 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Meta.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Meta.cs @@ -11,6 +11,8 @@ public class Pack1Meta public string Encryption { get; set; } + public string Compression { get; set; } + public string Iv { get; set; } public FileEntry[] Files { get; set; } @@ -66,6 +68,9 @@ public override string ToString() public const string DirectoryFileType = "directory"; public const string SymlinkFileType = "symlink"; + public const string XZCompression = "xz"; + public const string GZipCompression = "gzip"; + #endregion } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Unarchiver.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Unarchiver.cs index e26717fb..5c88e76e 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Unarchiver.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/Pack1Unarchiver.cs @@ -4,12 +4,16 @@ using System.Text; using Ionic.Zlib; using PatchKit.Network; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Data; using PatchKit.Unity.Patcher.Debug; using PatchKit.Unity.Utilities; +using SharpCompress.Compressors.LZMA; +using SharpCompress.Compressors.Xz; using SharpRaven; using SharpRaven.Data; +using SharpRaven.Utilities; namespace PatchKit.Unity.Patcher.AppData.Local { @@ -20,6 +24,8 @@ namespace PatchKit.Unity.Patcher.AppData.Local /// public class Pack1Unarchiver : IUnarchiver { + private delegate Stream DecompressorCreator(Stream source); + private static readonly DebugLogger DebugLogger = new DebugLogger(typeof(Pack1Unarchiver)); private readonly string _packagePath; @@ -48,7 +54,7 @@ public Pack1Unarchiver(string packagePath, Pack1Meta metaData, string destinatio // do nothing } - public Pack1Unarchiver(string packagePath, Pack1Meta metaData, string destinationDirPath, byte[] key, string suffix, BytesRange range) + private Pack1Unarchiver(string packagePath, Pack1Meta metaData, string destinationDirPath, byte[] key, string suffix, BytesRange range) { Checks.ArgumentFileExists(packagePath, "packagePath"); Checks.ArgumentDirectoryExists(destinationDirPath, "destinationDirPath"); @@ -81,7 +87,7 @@ public Pack1Unarchiver(string packagePath, Pack1Meta metaData, string destinatio public void Unarchive(CancellationToken cancellationToken) { int entry = 1; - + DebugLogger.Log("Unpacking " + _metaData.Files.Length + " files..."); foreach (var file in _metaData.Files) { @@ -147,7 +153,7 @@ private void Unpack(Pack1Meta.FileEntry file, Action progress, Cancellat break; case Pack1Meta.DirectoryFileType: progress(0.0); - UnpackDirectory(file); + UnpackDirectory(file, cancellationToken); progress(1.0); break; case Pack1Meta.SymlinkFileType: @@ -162,12 +168,12 @@ private void Unpack(Pack1Meta.FileEntry file, Action progress, Cancellat } - private void UnpackDirectory(Pack1Meta.FileEntry file) + private void UnpackDirectory(Pack1Meta.FileEntry file, CancellationToken cancellationToken) { string destPath = Path.Combine(_destinationDirPath, file.Name); DebugLogger.Log("Creating directory " + destPath); - Directory.CreateDirectory(destPath); + DirectoryOperations.CreateDirectory(destPath, cancellationToken); DebugLogger.Log("Directory " + destPath + " created successfully!"); } @@ -178,14 +184,39 @@ private void UnpackSymlink(Pack1Meta.FileEntry file) // TODO: how to create a symlink? } + private DecompressorCreator ResolveDecompressor(Pack1Meta meta) + { + switch (meta.Compression) + { + case Pack1Meta.XZCompression: + return CreateXzDecompressor; + + case Pack1Meta.GZipCompression: + return CreateGzipDecompressor; + + default: + return CreateGzipDecompressor; + } + } + private void UnpackRegularFile(Pack1Meta.FileEntry file, Action onProgress, CancellationToken cancellationToken, string destinationDirPath = null) { string destPath = Path.Combine(destinationDirPath == null ? _destinationDirPath : destinationDirPath, file.Name + _suffix); DebugLogger.LogFormat("Unpacking regular file {0} to {1}", file, destPath); + if (file.Size == null) + { + throw new NullReferenceException("File size cannot be null for regular file."); + } + + if (file.Offset == null) + { + throw new NullReferenceException("File offset cannot be null for regular file."); + } + Files.CreateParents(destPath); - RijndaelManaged rijn = new RijndaelManaged + var rijn = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, @@ -193,6 +224,7 @@ private void UnpackRegularFile(Pack1Meta.FileEntry file, Action onProgre }; ICryptoTransform decryptor = rijn.CreateDecryptor(_key, _iv); + DecompressorCreator decompressorCreator = ResolveDecompressor(_metaData); using (var fs = new FileStream(_packagePath, FileMode.Open)) { @@ -202,7 +234,7 @@ private void UnpackRegularFile(Pack1Meta.FileEntry file, Action onProgre { using (var target = new FileStream(destPath, FileMode.Create)) { - ExtractFileFromStream(limitedStream, target, file, decryptor, onProgress, cancellationToken); + ExtractFileFromStream(limitedStream, target, file.Size.Value, decryptor, decompressorCreator, onProgress, cancellationToken); } if (Platform.IsPosix()) @@ -215,45 +247,72 @@ private void UnpackRegularFile(Pack1Meta.FileEntry file, Action onProgre DebugLogger.Log("File " + file.Name + " unpacked successfully!"); } + private Stream CreateXzDecompressor(Stream source) + { + if (source.CanSeek) + { + return new XZStream(source); + } + else + { + return new XZStream(new PositionAwareStream(source)); + } + } + + private Stream CreateGzipDecompressor(Stream source) + { + return new GZipStream(source, CompressionMode.Decompress); + } + private void ExtractFileFromStream( - Stream sourceStream, + LimitedStream sourceStream, Stream targetStream, - Pack1Meta.FileEntry file, + long fileSize, ICryptoTransform decryptor, + DecompressorCreator createDecompressor, Action onProgress, CancellationToken cancellationToken) { using (var cryptoStream = new CryptoStream(sourceStream, decryptor, CryptoStreamMode.Read)) { - using (var gzipStream = new GZipStream(cryptoStream, Ionic.Zlib.CompressionMode.Decompress)) + using (var wrapperStream = new GZipReadWrapperStream(cryptoStream)) { - try + using (Stream decompressionStream = createDecompressor(wrapperStream)) { - long bytesProcessed = 0; - const int bufferSize = 128 * 1024; - var buffer = new byte[bufferSize]; - int count; - while ((count = gzipStream.Read(buffer, 0, bufferSize)) != 0) + try { - targetStream.Write(buffer, 0, count); - bytesProcessed += count; - onProgress(bytesProcessed / (double) file.Size.Value); + const int bufferSize = 128 * 1024; + var buffer = new byte[bufferSize]; + int count; + + while ((count = decompressionStream.Read(buffer, 0, buffer.Length)) > 0) + { + cancellationToken.ThrowIfCancellationRequested(); + targetStream.Write(buffer, 0, count); + + long bytesProcessed = sourceStream.Limit - sourceStream.BytesLeft; + onProgress(bytesProcessed / (double) fileSize); + } } - } - catch (Exception e) - { - DebugLogger.LogException(e); + catch (OperationCanceledException) + { + throw; + } + catch (Exception e) + { + DebugLogger.LogException(e); - var logManager = PatcherLogManager.Instance; - PatcherLogSentryRegistry sentryRegistry = logManager.SentryRegistry; - RavenClient ravenClient = sentryRegistry.RavenClient; + PatcherLogManager logManager = PatcherLogManager.Instance; + PatcherLogSentryRegistry sentryRegistry = logManager.SentryRegistry; + RavenClient ravenClient = sentryRegistry.RavenClient; - var sentryEvent = new SentryEvent(e); - PatcherLogSentryRegistry.AddDataToSentryEvent(sentryEvent, logManager.Storage.Guid.ToString()); + var sentryEvent = new SentryEvent(e); + PatcherLogSentryRegistry.AddDataToSentryEvent(sentryEvent, logManager.Storage.Guid.ToString()); + ravenClient.Capture(sentryEvent); - ravenClient.Capture(sentryEvent); + throw; + } - throw; } } } diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/PositionAwareStream.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/PositionAwareStream.cs new file mode 100644 index 00000000..64357691 --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/PositionAwareStream.cs @@ -0,0 +1,95 @@ + +using System; +using System.IO; + +namespace PatchKit.Unity.Patcher.AppData.Local +{ + public class PositionAwareStream : Stream + { + private readonly Stream _baseStream; + + private long _position = 0; + + public PositionAwareStream(Stream stream) + { + if (stream.CanSeek) + { + throw new ArgumentException("Redundant use of position aware stream."); + } + + _baseStream = stream; + } + + public override bool CanRead { get { return _baseStream.CanRead; } } + + public override bool CanSeek { get { return true; } } + + public override bool CanWrite { get { return false; } } + + public override long Length { get { return _baseStream.Length; } } + + public override long Position + { + get + { + return _position; + } + set + { + if (value < _position) + { + throw new NotSupportedException("Cannot seek back"); + } + + long bytesForward = _position - value; + for (int i = 0; i < bytesForward; i++) + { + _baseStream.ReadByte(); + } + + _position = value; + } + } + + public override void Flush() + { + _baseStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesRead = _baseStream.Read(buffer, offset, count); + _position += bytesRead; + return bytesRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + Position = offset; + break; + + case SeekOrigin.Current: + Position = Position + offset; + break; + + case SeekOrigin.End: + throw new NotSupportedException(); + } + + return Position; + } + + public override void SetLength(long value) + { + _baseStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/Assets/Editor/AssetStoreBatchMode/AssetStoreBatchMode.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Local/PositionAwareStream.cs.meta similarity index 76% rename from Assets/Editor/AssetStoreBatchMode/AssetStoreBatchMode.cs.meta rename to Assets/PatchKit Patcher/Scripts/AppData/Local/PositionAwareStream.cs.meta index 270a7b50..aad11dd0 100644 --- a/Assets/Editor/AssetStoreBatchMode/AssetStoreBatchMode.cs.meta +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/PositionAwareStream.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 3f4d7d24eb966401b822c976b5b2c922 -timeCreated: 1490992925 +guid: e0e6518f51e1e40a4b5227c75603ffd6 +timeCreated: 1531902940 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Local/TemporaryDirectory.cs b/Assets/PatchKit Patcher/Scripts/AppData/Local/TemporaryDirectory.cs index ffa60065..966230e9 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Local/TemporaryDirectory.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Local/TemporaryDirectory.cs @@ -1,6 +1,8 @@ using System; using System.IO; using JetBrains.Annotations; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; namespace PatchKit.Unity.Patcher.AppData.Local @@ -24,13 +26,13 @@ private TemporaryDirectory([NotNull] string path) } Path = path; - + if (Directory.Exists(Path)) { - Directory.Delete(Path, true); + DirectoryOperations.Delete(Path, CancellationToken.Empty, true); } - Directory.CreateDirectory(Path); + DirectoryOperations.CreateDirectory(Path, CancellationToken.Empty); } //TODO: Move it to some extension method. @@ -61,7 +63,7 @@ private void ReleaseUnmanagedResources() { if (!_keep && Directory.Exists(Path)) { - Directory.Delete(Path, true); + DirectoryOperations.Delete(Path, CancellationToken.Empty, true); } } @@ -98,7 +100,7 @@ public static void ExecuteIn(string tempDirName, Action acti private static bool ShouldKeepFilesOnError() { string value = null; - + if (EnvironmentInfo.TryReadEnvironmentVariable(EnvironmentVariables.KeepFilesOnErrorEnvironmentVariable, out value)) { return !(string.IsNullOrEmpty(value) || value == "0"); diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/BaseHttpDownloader.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/BaseHttpDownloader.cs index 65e1ec05..01182cb5 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/BaseHttpDownloader.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/BaseHttpDownloader.cs @@ -6,6 +6,7 @@ using PatchKit.Network; using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; +using PatchKit.Unity.Utilities; namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders { @@ -13,7 +14,7 @@ public sealed class BaseHttpDownloader : IBaseHttpDownloader { private readonly ILogger _logger; - private const int BufferSize = 5 * 1024 * 1024; + private static readonly int BufferSize = 5 * (int) Units.MB; private readonly string _url; private readonly int _timeout; diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedFileStream.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedFileStream.cs index b896005a..10649ff4 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedFileStream.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedFileStream.cs @@ -40,7 +40,6 @@ public enum WorkFlags private int _chunkIndex; private int _startChunk; - private int _endChunk; private FileStream _fileStream; @@ -84,7 +83,6 @@ public ChunkedFileStream([NotNull] string path, long fileSize, ChunksData chunks _logger.LogTrace("chunksData.ChunkSize = " + chunksData.ChunkSize); _startChunk = startChunk; - _endChunk = endChunk; bool noEndChunk = endChunk == -1; bool isLastChunkIncomplete = endChunk * chunksData.ChunkSize > fileSize; diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedHttpDownloader.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedHttpDownloader.cs index 9fdd9687..9eac28de 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedHttpDownloader.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ChunkedHttpDownloader.cs @@ -7,6 +7,7 @@ using PatchKit.Api.Models.Main; using PatchKit.Logging; using PatchKit.Network; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.Debug; using PatchKit.Unity.Utilities; using CancellationToken = PatchKit.Unity.Patcher.Cancellation.CancellationToken; @@ -68,12 +69,12 @@ public ChunkedHttpDownloader([NotNull] string destinationFilePath, [NotNull] Res _size = size; } - private ChunkedFileStream OpenFileStream() + private ChunkedFileStream OpenFileStream(CancellationToken cancellationToken) { var parentDirectory = Path.GetDirectoryName(_destinationFilePath); if (!string.IsNullOrEmpty(parentDirectory)) { - Directory.CreateDirectory(parentDirectory); + DirectoryOperations.CreateDirectory(parentDirectory, cancellationToken); } var chunksRange = CalculateContainingChunksRange(_range); @@ -118,7 +119,7 @@ public void Download(CancellationToken cancellationToken) Assert.MethodCalledOnlyOnce(ref _downloadHasBeenCalled, "Download"); - using (var fileStream = OpenFileStream()) + using (var fileStream = OpenFileStream(cancellationToken)) { bool retry; @@ -288,7 +289,7 @@ public static IEnumerable BuildDownloadJobQueue(ResourceUrl resourc int lastPart = totalPartCount; - + if (bounds.End != -1) { lastPart = (int) (bounds.End / partSize); diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/HttpDownloader.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/HttpDownloader.cs index 3b80adff..23be798d 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/HttpDownloader.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/HttpDownloader.cs @@ -5,6 +5,7 @@ using JetBrains.Annotations; using PatchKit.Logging; using PatchKit.Network; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; using PatchKit.Unity.Utilities; @@ -40,12 +41,12 @@ public HttpDownloader([NotNull] string destinationFilePath, [NotNull] string[] u _urls = urls; } - private FileStream OpenFileStream() + private FileStream OpenFileStream(CancellationToken cancellationToken) { var parentDirectory = Path.GetDirectoryName(_destinationFilePath); if (!string.IsNullOrEmpty(parentDirectory)) { - Directory.CreateDirectory(parentDirectory); + DirectoryOperations.CreateDirectory(parentDirectory, cancellationToken); } return new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None); @@ -64,7 +65,7 @@ public void Download(CancellationToken cancellationToken) Assert.MethodCalledOnlyOnce(ref _downloadHasBeenCalled, "Download"); - using (var fileStream = OpenFileStream()) + using (var fileStream = OpenFileStream(cancellationToken)) { bool retry; diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentClientProcessStartInfoProvider.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentClientProcessStartInfoProvider.cs deleted file mode 100644 index cbb7f267..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentClientProcessStartInfoProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Diagnostics; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders -{ - public interface ITorrentClientProcessStartInfoProvider - { - ProcessStartInfo GetProcessStartInfo(); - } -} diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentClientProcessStartInfoProvider.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentClientProcessStartInfoProvider.cs.meta deleted file mode 100644 index 0fe90b72..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentClientProcessStartInfoProvider.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 8e5c24fea95c23d4f8ad3fdf329485ff -timeCreated: 1498269163 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentDownloader.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentDownloader.cs deleted file mode 100644 index 78d4ee17..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentDownloader.cs +++ /dev/null @@ -1,11 +0,0 @@ -using PatchKit.Unity.Patcher.Cancellation; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders -{ - public interface ITorrentDownloader - { - event DownloadProgressChangedHandler DownloadProgressChanged; - - void Download(CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentDownloader.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentDownloader.cs.meta deleted file mode 100644 index 22364185..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/ITorrentDownloader.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 9b86b307bae4e01418bba778ef96ceb8 -timeCreated: 1485022868 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClient.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClient.cs deleted file mode 100644 index 6b6839d2..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClient.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Text; -using JetBrains.Annotations; -using Newtonsoft.Json; -using PatchKit.Logging; -using PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents; -using PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents.Protocol; -using PatchKit.Unity.Patcher.Cancellation; -using PatchKit.Unity.Patcher.Debug; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders -{ - /// - /// Provides an easy access for torrent client program. - /// - /// - public sealed class TorrentClient : IDisposable - { - private readonly ILogger _logger; - - private readonly ITorrentClientProcessStartInfoProvider _processStartInfoProvider; - - private readonly Process _process; - - private readonly StreamReader _stdOutput; - - private readonly StreamWriter _stdInput; - - private bool _disposed; - - public TorrentClient(ITorrentClientProcessStartInfoProvider processStartInfoProvider) - { - _processStartInfoProvider = processStartInfoProvider; - - _logger = PatcherLogManager.DefaultLogger; - _process = StartProcess(); - _stdOutput = CreateStdOutputStream(); - _stdInput = CreateStdInputStream(); - } - - private static string ConvertPathForTorrentClient(string path) - { - return path.Replace("\\", "/").Replace(" ", "\\ "); - } - - public TorrentClientStatus GetStatus(CancellationToken cancellationToken) - { - try - { - _logger.LogDebug("Getting status..."); - - var result = ExecuteCommand("status", cancellationToken); - - _logger.LogDebug("Getting status finished."); - - return result; - } - catch (Exception e) - { - _logger.LogError("Getting status failed.", e); - throw; - } - } - - public void AddTorrent(string torrentFilePath, string downloadDirectoryPath, - CancellationToken cancellationToken) - { - try - { - _logger.LogDebug(string.Format("Adding torrent from {0}...", torrentFilePath)); - _logger.LogTrace("downloadDirectoryPath = " + downloadDirectoryPath); - - var convertedTorrentFilePath = ConvertPathForTorrentClient(torrentFilePath); - var convertedDownloadDirectoryPath = ConvertPathForTorrentClient(downloadDirectoryPath); - - _logger.LogTrace("convertedTorrentFilePath = " + convertedTorrentFilePath); - _logger.LogTrace("convertedDownloadDirectoryPath = " + convertedDownloadDirectoryPath); - - var command = string.Format("add-torrent {0} {1}", convertedTorrentFilePath, - convertedDownloadDirectoryPath); - - var result = ExecuteCommand(command, cancellationToken); - - _logger.LogTrace("result.Message = " + result.Message); - _logger.LogTrace("result.Status = " + result.Status); - - if (result.Status != "ok") - { - throw new AddTorrentFailureException( - string.Format("Invalid add-torrent status: {0}", result.Status)); - } - - _logger.LogDebug("Adding torrent finished."); - } - catch (Exception e) - { - _logger.LogError("Adding torrent failed.", e); - throw; - } - } - - private TResult ExecuteCommand([NotNull] string command, CancellationToken cancellationToken) - { - if (command == null) throw new ArgumentNullException("command"); - - _logger.LogDebug(string.Format("Executing command {0}", command)); - - _stdInput.WriteLine(command); - _stdInput.Flush(); - - string resultStr = ReadCommandResult(cancellationToken); - - _logger.LogDebug("Command execution finished. Parsing result..."); - _logger.LogTrace("result = " + resultStr); - - var result = JsonConvert.DeserializeObject(resultStr); - - _logger.LogDebug("Parsing finished."); - - return result; - } - - private string ReadCommandResult(CancellationToken cancellationToken) - { - var str = new StringBuilder(); - - while (!str.ToString().EndsWith("#=end")) - { - cancellationToken.ThrowIfCancellationRequested(); - - str.Append((char) _stdOutput.Read()); - } - - return str.ToString().Substring(0, str.Length - 5); - } - - private StreamReader CreateStdOutputStream() - { - return new StreamReader(_process.StandardOutput.BaseStream, CreateStdEncoding()); - } - - private StreamWriter CreateStdInputStream() - { - return new StreamWriter(_process.StandardInput.BaseStream, CreateStdEncoding()); - } - - private static Encoding CreateStdEncoding() - { - return new UTF8Encoding(false); - } - - private Process StartProcess() - { - var processStartInfo = _processStartInfoProvider.GetProcessStartInfo(); - _logger.LogTrace("processStartInfo.FileName" + processStartInfo.FileName); - _logger.LogTrace("processStartInfo.Arguments" + processStartInfo.Arguments); - - _logger.LogDebug("Starting torrent-client process..."); - var process = Process.Start(processStartInfo); - _logger.LogDebug("torrent-client process started."); - - return process; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~TorrentClient() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - _stdOutput.Dispose(); - _stdInput.Dispose(); - - try - { - _logger.LogDebug("Killing torrent-client process..."); - - if (!_process.HasExited) - { - _logger.LogDebug("Sending kill request and waiting one second..."); - _process.Kill(); - _process.WaitForExit(1000); - if (_process.HasExited) - { - _logger.LogDebug("torrent-client process killed."); - } - else - { - _logger.LogWarning( - "torrent-client process hasn't been killed. Ignoring in order to not freeze application execution."); - } - } - else - { - _logger.LogDebug("torrent-client process is already killed."); - } - } - catch (Exception e) - { - _logger.LogError("Killing torrent-client process failed.", e); - throw; - } - finally - { - _process.Dispose(); - } - } - - _disposed = true; - } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClientException.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClientException.cs deleted file mode 100644 index a3a9cf12..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClientException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders -{ - public class TorrentClientException : Exception - { - public TorrentClientException(string message) : base(message) - { - } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentDownloader.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentDownloader.cs deleted file mode 100644 index e31efe1b..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentDownloader.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using JetBrains.Annotations; -using PatchKit.Unity.Patcher.Cancellation; -using PatchKit.Unity.Patcher.Debug; -using PatchKit.Logging; -using PatchKit.Unity.Patcher.AppData.Local; -using PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents.Protocol; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders -{ - /// - /// Downloads file through torrents by using . - /// - /// - public sealed class TorrentDownloader : ITorrentDownloader - { - private const int UpdateInterval = 1000; - - private const int ConnectionTimeout = 10000; - - private readonly ILogger _logger; - - private readonly string _destinationFilePath; - private readonly string _torrentFilePath; - private readonly long _totalBytes; - - private bool _downloadHasBeenCalled; - - public event DownloadProgressChangedHandler DownloadProgressChanged; - - private string DestinationDirectoryPath - { - get { return _destinationFilePath + ".torrent_dir"; } - } - - public TorrentDownloader([NotNull] string destinationFilePath, [NotNull] string torrentFilePath, - long totalBytes) - { - if (destinationFilePath == null) throw new ArgumentNullException("destinationFilePath"); - if (torrentFilePath == null) throw new ArgumentNullException("torrentFilePath"); - if (totalBytes <= 0) throw new ArgumentOutOfRangeException("totalBytes"); - - _logger = PatcherLogManager.DefaultLogger; - _destinationFilePath = destinationFilePath; - _torrentFilePath = torrentFilePath; - _totalBytes = totalBytes; - } - - public void Download(CancellationToken cancellationToken) - { - try - { - _logger.LogDebug("Downloading..."); - _logger.LogTrace("torrentFilePath = " + _torrentFilePath); - _logger.LogTrace("destinationFilePath = " + _destinationFilePath); - _logger.LogTrace("destinationDirectoryPath = " + DestinationDirectoryPath); - - Assert.MethodCalledOnlyOnce(ref _downloadHasBeenCalled, "Download"); - - if (!Directory.Exists(DestinationDirectoryPath)) - { - DirectoryOperations.CreateDirectory(DestinationDirectoryPath); - } - - _logger.LogTrace("download dir = " + DestinationDirectoryPath); - - using (var torrentClient = new TorrentClient(new UnityTorrentClientProcessStartInfoProvider())) - { - torrentClient.AddTorrent(_torrentFilePath, DestinationDirectoryPath, cancellationToken); - - var timeoutWatch = new Stopwatch(); - timeoutWatch.Start(); - - TorrentStatus status = GetAndCheckTorrentStatus(torrentClient, cancellationToken); - double initialProgress = status.Progress; - _logger.LogTrace("initialProgress = " + status.Progress); - var waitHandle = new AutoResetEvent(false); - - OnDownloadProgressChanged(0); - - using (cancellationToken.Register(() => waitHandle.Set())) - { - bool finished = false; - - do - { - cancellationToken.ThrowIfCancellationRequested(); - - status = GetAndCheckTorrentStatus(torrentClient, cancellationToken); - - _logger.LogTrace("progress = " + status.Progress); - - CheckTimeout(timeoutWatch, status.Progress, initialProgress); - - OnDownloadProgressChanged((long) (_totalBytes * status.Progress)); - - if (status.IsSeeding) - { - finished = true; - } - else - { - waitHandle.WaitOne(UpdateInterval); - } - } while (!finished); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - string downloadedFilePath = GetDownloadedFilePath(); - - if (File.Exists(_destinationFilePath)) - { - File.Delete(_destinationFilePath); - } - - File.Move(downloadedFilePath, _destinationFilePath); - } - catch (Exception e) - { - _logger.LogError("Downloading has failed.", e); - throw; - } - } - - private TorrentStatus GetAndCheckTorrentStatus(TorrentClient torrentClient, - CancellationToken cancellationToken) - { - var torrentClientStatus = torrentClient.GetStatus(cancellationToken); - - _logger.LogTrace("status = " + torrentClientStatus.Status); - - if (torrentClientStatus.Status != "ok") - { - throw new DownloadFailureException("Torrent client failure."); - } - - Assert.IsNotNull(torrentClientStatus.Data); - Assert.IsNotNull(torrentClientStatus.Data.Torrents); - Assert.AreEqual(1, torrentClientStatus.Data.Torrents.Length); - - var torrentStatus = torrentClientStatus.Data.Torrents[0]; - - if (!string.IsNullOrEmpty(torrentStatus.Error)) - { - throw new DownloadFailureException("Torrent client failure: " + torrentStatus.Error); - } - - return torrentStatus; - } - - private void CheckTimeout(Stopwatch timeoutWatch, double progress, double initialProgress) - { - if (Math.Abs(progress - initialProgress) > 0.0001) - { - return; - } - - if (timeoutWatch.ElapsedMilliseconds < ConnectionTimeout) - { - return; - } - - throw new DownloadFailureException("Torrent downloading has timed out."); - } - - private string GetDownloadedFilePath() - { - var dirInfo = new DirectoryInfo(DestinationDirectoryPath); - - var dirFiles = dirInfo.GetFiles(); - - if (dirFiles.Length != 1) - { - throw new DownloadFailureException(string.Format( - "Invalid downloaded torrent directory structure. It contains {0} files instead of one.", - dirFiles.Length)); - } - - return dirFiles[0].FullName; - } - - private void OnDownloadProgressChanged(long downloadedbytes) - { - var handler = DownloadProgressChanged; - if (handler != null) handler(downloadedbytes); - } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentDownloader.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentDownloader.cs.meta deleted file mode 100644 index 35a8a29f..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentDownloader.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: afe2b1e3bb3fa2b408a331e260497eec -timeCreated: 1482174088 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents.meta deleted file mode 100644 index 88eb2ab8..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 8abecac7901344d4b0ac399d05dbaf8f -timeCreated: 1515766405 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/AddTorrentFailureException.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/AddTorrentFailureException.cs deleted file mode 100644 index 3580c223..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/AddTorrentFailureException.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents -{ - [Serializable] - public class AddTorrentFailureException : Exception - { - public AddTorrentFailureException() - { - } - - public AddTorrentFailureException(string message) : base(message) - { - } - - public AddTorrentFailureException(string message, Exception inner) : base(message, inner) - { - } - - protected AddTorrentFailureException( - SerializationInfo info, - StreamingContext context) : base(info, context) - { - } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/AddTorrentFailureException.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/AddTorrentFailureException.cs.meta deleted file mode 100644 index ff44f4b9..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/AddTorrentFailureException.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 95dc43c0d6c1416889ba0cef03409e3c -timeCreated: 1515769402 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol.meta deleted file mode 100644 index ee59c7b4..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 55a5927dc52a41978da52443d09088db -timeCreated: 1515766424 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientMessage.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientMessage.cs deleted file mode 100644 index 469bf050..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientMessage.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Newtonsoft.Json; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents.Protocol -{ - public class TorrentClientMessage - { - [JsonProperty("message")] - public string Message { get; set; } - - [JsonProperty("status")] - public string Status { get; set; } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientMessage.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientMessage.cs.meta deleted file mode 100644 index 8c380010..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientMessage.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 678a773d4a2f4d54a2e7425f83823c3b -timeCreated: 1515768277 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatus.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatus.cs deleted file mode 100644 index 37e14e5b..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatus.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Newtonsoft.Json; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents.Protocol -{ - public class TorrentClientStatus - { - [JsonProperty("data")] - public TorrentClientStatusData Data { get; set; } - - [JsonProperty("status")] - public string Status { get; set; } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatus.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatus.cs.meta deleted file mode 100644 index 8132fb16..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatus.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: bc9be5d6b76949958a430234b7ffe428 -timeCreated: 1515766572 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatusData.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatusData.cs deleted file mode 100644 index 9ee5a5ab..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatusData.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Newtonsoft.Json; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents.Protocol -{ - public class TorrentClientStatusData - { - [JsonProperty("count")] - public int Count { get; set; } - - [JsonProperty("torrents")] - public TorrentStatus[] Torrents { get; set; } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatusData.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatusData.cs.meta deleted file mode 100644 index 4317dec7..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentClientStatusData.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 0e63db4466094dec90bb03d6e60295c7 -timeCreated: 1515768055 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentStatus.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentStatus.cs deleted file mode 100644 index bd844660..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentStatus.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Newtonsoft.Json; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders.Torrents.Protocol -{ - public class TorrentStatus - { - [JsonProperty("error")] - public string Error { get; set; } - - [JsonProperty("is_seeding")] - public bool IsSeeding { get; set; } - - [JsonProperty("last_scrape")] - public long LastScrape { get; set; } - - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("paused")] - public bool Paused { get; set; } - - [JsonProperty("peers")] - public long Peers { get; set; } - - [JsonProperty("progress")] - public double Progress { get; set; } - - [JsonProperty("seed_rank")] - public long SeedRank { get; set; } - - [JsonProperty("seeding_time")] - public long SeedingTime { get; set; } - - [JsonProperty("seeds")] - public long Seeds { get; set; } - - [JsonProperty("total_upload")] - public long TotalUpload { get; set; } - - [JsonProperty("total_wanted")] - public long TotalWanted { get; set; } - } -} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentStatus.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentStatus.cs.meta deleted file mode 100644 index 99a77be6..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/Torrents/Protocol/TorrentStatus.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 67e74a90b2ff42f8ab9fd464301f76ef -timeCreated: 1515766446 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/UnityTorrentClientProcessStartInfoProvider.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/UnityTorrentClientProcessStartInfoProvider.cs deleted file mode 100644 index 2fbbd3e3..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/UnityTorrentClientProcessStartInfoProvider.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using PatchKit.Unity.Utilities; -using UnityEngine; - -namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders -{ - public class UnityTorrentClientProcessStartInfoProvider : ITorrentClientProcessStartInfoProvider - { - private const string TorrentClientWinPath = "torrent-client/win/torrent-client.exe"; - private const string TorrentClientOsx64Path = "torrent-client/osx64/torrent-client"; - private const string TorrentClientLinux64Path = "torrent-client/linux64/torrent-client"; - - private string _streamingAssetsPath; - - public UnityTorrentClientProcessStartInfoProvider() - { - UnityDispatcher.Invoke(() => - { - _streamingAssetsPath = Application.streamingAssetsPath; - }).WaitOne(); - } - - public ProcessStartInfo GetProcessStartInfo() - { - if (Platform.IsWindows()) - { - var processStartInfo = new ProcessStartInfo - { - FileName = _streamingAssetsPath.PathCombine(TorrentClientWinPath), - RedirectStandardInput = true, - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - }; - - return processStartInfo; - } - - if (Platform.IsOSX()) - { - var processStartInfo = new ProcessStartInfo - { - FileName = _streamingAssetsPath.PathCombine(TorrentClientOsx64Path), - RedirectStandardInput = true, - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - }; - - // make sure that binary can be executed - Chmod.SetExecutableFlag(processStartInfo.FileName); - - processStartInfo.EnvironmentVariables["DYLD_LIBRARY_PATH"] = Path.Combine(_streamingAssetsPath, "torrent-client/osx64"); - - return processStartInfo; - } - - if (Platform.IsLinux() && IntPtr.Size == 8) // Linux 64 bit - { - var processStartInfo = new ProcessStartInfo - { - FileName = _streamingAssetsPath.PathCombine(TorrentClientLinux64Path), - RedirectStandardInput = true, - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - }; - - // make sure that binary can be executed - Chmod.SetExecutableFlag(processStartInfo.FileName); - - processStartInfo.EnvironmentVariables["LD_LIBRARY_PATH"] = Path.Combine(_streamingAssetsPath, "torrent-client/linux64"); - - return processStartInfo; - } - - throw new TorrentClientException("Unsupported platform by torrent-client."); - } - } -} diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/UnityTorrentClientProcessStartInfoProvider.cs.meta b/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/UnityTorrentClientProcessStartInfoProvider.cs.meta deleted file mode 100644 index e660bdcb..00000000 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/UnityTorrentClientProcessStartInfoProvider.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 7e63dd1ccaf93144d978ba0a0aa44aa4 -timeCreated: 1498269163 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/IRemoteMetaData.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/IRemoteMetaData.cs index bfe34dac..3c902dd3 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/IRemoteMetaData.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/IRemoteMetaData.cs @@ -26,6 +26,12 @@ public interface IRemoteMetaData /// The version identifier. AppDiffSummary GetDiffSummary(int versionId); + /// + /// Returns the AppVersion model for the specified version id. + /// + /// The version identifier. + AppVersion GetAppVersionInfo(int versionId); + /// /// Returns key secret for certain key. /// diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteData.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteData.cs index 0205747a..fc843a42 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteData.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteData.cs @@ -55,13 +55,11 @@ public RemoteResource GetContentPackageResource(int versionId, string keySecret, RemoteResource resource = new RemoteResource(); var summary = _mainApiConnection.GetAppVersionContentSummary(_appSecret, versionId); - var torrentUrl = _mainApiConnection.GetAppVersionContentTorrentUrl(_appSecret, versionId, keySecret); var urls = _mainApiConnection.GetAppVersionContentUrls(_appSecret, versionId, countryCode, keySecret); resource.Size = summary.Size; resource.HashCode = summary.HashCode; resource.ChunksData = ConvertToChunksData(summary.Chunks); - resource.TorrentUrls = new[] {torrentUrl.Url}; resource.ResourceUrls = urls; return resource; @@ -78,13 +76,11 @@ public RemoteResource GetDiffPackageResource(int versionId, string keySecret, st RemoteResource resource = new RemoteResource(); var summary = _mainApiConnection.GetAppVersionDiffSummary(_appSecret, versionId); - var torrentUrl = _mainApiConnection.GetAppVersionDiffTorrentUrl(_appSecret, versionId, keySecret); var urls = _mainApiConnection.GetAppVersionDiffUrls(_appSecret, versionId, countryCode, keySecret); resource.Size = summary.Size; resource.HashCode = summary.HashCode; resource.ChunksData = ConvertToChunksData(summary.Chunks); - resource.TorrentUrls = new[] { torrentUrl.Url }; resource.ResourceUrls = urls; return resource; } diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteMetaData.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteMetaData.cs index faba6288..9542fb88 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteMetaData.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteMetaData.cs @@ -59,7 +59,9 @@ public int GetLatestVersionId(bool retryRequests = true) DebugLogger.Log("Getting latest version id."); DebugLogger.Log("retryRequests = " + retryRequests); var m = retryRequests ? _mainApiConnection : _mainApiConnectionWithoutRetry; +#pragma warning disable 612 return m.GetAppLatestAppVersionId(_appSecret).Id; +#pragma warning restore 612 } public Api.Models.Main.App GetAppInfo(bool retryRequests = true) @@ -95,5 +97,17 @@ public string GetKeySecret(string key, string cachedKeySecret) return keySecret; } + + public AppVersion GetAppVersionInfo(int versionId) + { + if (versionId <= 0) + { + throw new ArgumentException("Version id is invalid.", "versionId"); + } + + DebugLogger.Log(string.Format("Getting app version info for version with id {0}", versionId)); + + return _mainApiConnection.GetAppVersion(_appSecret, versionId); + } } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResource.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResource.cs index 112dc411..dd4509e1 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResource.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResource.cs @@ -6,7 +6,6 @@ namespace PatchKit.Unity.Patcher.AppData.Remote { public struct RemoteResource { - public string[] TorrentUrls; public ResourceUrl[] ResourceUrls; @@ -19,7 +18,6 @@ public struct RemoteResource public override string ToString() { return "urls: {" + string.Join(", ", GetUrls()) + "}\n" + - "torrent urls: {" + string.Join(", ", TorrentUrls) + "}\n" + "size: " + Size + "\n" + "hashcode: " + HashCode; } @@ -34,7 +32,7 @@ public bool HasMetaUrls() public string[] GetMetaUrls() { var urls = new List(); - + foreach (ResourceUrl resourceUrl in ResourceUrls) { if (!string.IsNullOrEmpty(resourceUrl.MetaUrl)) @@ -50,7 +48,7 @@ public string[] GetMetaUrls() public string[] GetUrls() { var urls = new List(); - + foreach (ResourceUrl resourceUrl in ResourceUrls) { if (!string.IsNullOrEmpty(resourceUrl.Url)) diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResourceDownloader.cs b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResourceDownloader.cs index e05b70b0..8fd89483 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResourceDownloader.cs +++ b/Assets/PatchKit Patcher/Scripts/AppData/Remote/RemoteResourceDownloader.cs @@ -19,11 +19,6 @@ public delegate IChunkedHttpDownloader CreateNewChunkedHttpDownloader([NotNull] [NotNull] ResourceUrl[] urls, ChunksData chunksData, long size); - public delegate ITorrentDownloader CreateNewTorrentDownloader([NotNull] string destinationFilePath, - [NotNull] string torrentFilePath, - long totalBytes); - - private readonly ILogger _logger; private readonly string _destinationFilePath; @@ -31,28 +26,23 @@ public delegate ITorrentDownloader CreateNewTorrentDownloader([NotNull] string d private readonly RemoteResource _resource; - private readonly bool _useTorrents; private readonly CreateNewHttpDownloader _createNewHttpDownloader; private readonly CreateNewChunkedHttpDownloader _createNewChunkedHttpDownloader; - private readonly CreateNewTorrentDownloader _createNewTorrentDownloader; private bool _downloadHasBeenCalled; public event DownloadProgressChangedHandler DownloadProgressChanged; - public RemoteResourceDownloader(string destinationFilePath, string destinationMetaPath, RemoteResource resource, - bool useTorrents) : - this(destinationFilePath, destinationMetaPath, resource, useTorrents, CreateDefaultHttpDownloader, - CreateDefaultChunkedHttpDownloader, CreateDefaultTorrentDownloader) + public RemoteResourceDownloader(string destinationFilePath, string destinationMetaPath, RemoteResource resource) + : this(destinationFilePath, destinationMetaPath, resource, CreateDefaultHttpDownloader, + CreateDefaultChunkedHttpDownloader) { } public RemoteResourceDownloader([NotNull] string destinationFilePath, [NotNull] string destinationMetaPath, RemoteResource resource, - bool useTorrents, CreateNewHttpDownloader createNewHttpDownloader, - CreateNewChunkedHttpDownloader createNewChunkedHttpDownloader, - CreateNewTorrentDownloader createNewTorrentDownloader) + CreateNewChunkedHttpDownloader createNewChunkedHttpDownloader) { if (destinationFilePath == null) throw new ArgumentNullException("destinationFilePath"); if (destinationMetaPath == null) throw new ArgumentNullException("destinationMetaPath"); @@ -61,15 +51,8 @@ public RemoteResourceDownloader([NotNull] string destinationFilePath, [NotNull] _destinationFilePath = destinationFilePath; _destinationMetaPath = destinationMetaPath; _resource = resource; - _useTorrents = useTorrents; _createNewHttpDownloader = createNewHttpDownloader; _createNewChunkedHttpDownloader = createNewChunkedHttpDownloader; - _createNewTorrentDownloader = createNewTorrentDownloader; - } - - private string TorrentFilePath - { - get { return _destinationFilePath + ".torrent"; } } private void DownloadMeta(CancellationToken cancellationToken) @@ -82,30 +65,6 @@ private void DownloadMeta(CancellationToken cancellationToken) _logger.LogDebug("Resource meta downloaded."); } - private void DownloadTorrentFile(CancellationToken cancellationToken) - { - _logger.LogDebug("Downloading torrent file..."); - _logger.LogTrace("torrentFilePath = " + TorrentFilePath); - - var torrentFileDownloader = _createNewHttpDownloader(TorrentFilePath, _resource.TorrentUrls); - torrentFileDownloader.Download(cancellationToken); - - _logger.LogDebug("Torrent file downloaded."); - } - - private void DownloadWithTorrents(CancellationToken cancellationToken) - { - DownloadTorrentFile(cancellationToken); - - _logger.LogDebug("Downloading resource with torrents..."); - - var downloader = _createNewTorrentDownloader(_destinationFilePath, TorrentFilePath, _resource.Size); - downloader.DownloadProgressChanged += OnDownloadProgressChanged; - downloader.Download(cancellationToken); - - _logger.LogDebug("Resource has been downloaded with torrents."); - } - private void DownloadWithChunkedHttp(CancellationToken cancellationToken) { _logger.LogDebug("Downloading resource with chunked HTTP..."); @@ -163,25 +122,6 @@ public void Download(CancellationToken cancellationToken) _logger.LogDebug("Resource meta are not available."); } - if (_useTorrents) - { - _logger.LogDebug("Torrent downloading is enabled."); - - try - { - DownloadWithTorrents(cancellationToken); - return; - } - catch (DownloadFailureException e) - { - _logger.LogWarning("Failed to download resource with torrents. Falling back to other downloaders...", e); - } - } - else - { - _logger.LogDebug("Torrent downloading is disabled."); - } - if (AreChunksAvailable()) { _logger.LogDebug("Chunks are available."); @@ -232,12 +172,5 @@ private static IChunkedHttpDownloader CreateDefaultChunkedHttpDownloader([NotNul { return new ChunkedHttpDownloader(destinationFilePath, urls, chunksData, size); } - - private static ITorrentDownloader CreateDefaultTorrentDownloader([NotNull] string destinationFilePath, - [NotNull] string torrentFilePath, - long totalBytes) - { - return new TorrentDownloader(destinationFilePath, torrentFilePath, totalBytes); - } } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppStarter.cs b/Assets/PatchKit Patcher/Scripts/AppStarter.cs index 970240f0..7a0d5668 100644 --- a/Assets/PatchKit Patcher/Scripts/AppStarter.cs +++ b/Assets/PatchKit Patcher/Scripts/AppStarter.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using PatchKit.Api.Models.Main; using PatchKit.Unity.Patcher.AppData; using PatchKit.Unity.Patcher.Data; using PatchKit.Unity.Patcher.Debug; @@ -26,12 +27,51 @@ public AppStarter(App app) AppFinder = new AppFinder(); } + private string ResolveExecutablePath(AppVersion appVersion) + { + PlatformType platformType = Platform.GetPlatformType(); + + if (!string.IsNullOrEmpty(appVersion.MainExecutable)) + { + string executablePath = Path.Combine(_app.LocalDirectory.Path, appVersion.MainExecutable); + + bool isOSXApp = platformType == PlatformType.OSX && + executablePath.EndsWith(".app") && + Directory.Exists(executablePath); + + if (File.Exists(executablePath) || isOSXApp) + { + return executablePath; + } + + // Reports to Sentry + try + { + throw new FileNotFoundException(string.Format("Couldn't resolve executable in {0}", executablePath)); + } + catch (Exception e) + { + DebugLogger.LogException(e); + } + + } + + return AppFinder.FindExecutable(_app.LocalDirectory.Path, platformType); + } + public void Start() + { + var appVersion = _app.RemoteMetaData.GetAppVersionInfo(_app.GetInstalledVersionId()); + StartAppVersion(appVersion); + } + + private void StartAppVersion(AppVersion appVersion) { DebugLogger.Log("Starting application."); PlatformType platformType = Platform.GetPlatformType(); - string appFilePath = AppFinder.FindExecutable(_app.LocalDirectory.Path, platformType); + string appFilePath = ResolveExecutablePath(appVersion); + if (appFilePath == null) { throw new InvalidOperationException("Couldn't find executable."); @@ -52,7 +92,8 @@ public void Start() } } - var processStartInfo = GetProcessStartInfo(appFilePath, platformType); + var processStartInfo = GetProcessStartInfo(appFilePath, appVersion.MainExecutableArgs, platformType); + StartAppProcess(processStartInfo); } @@ -61,31 +102,42 @@ private bool NeedPermissionFix(PlatformType platformType) return platformType == PlatformType.OSX || platformType == PlatformType.Linux; } - private ProcessStartInfo GetProcessStartInfo(string executablePath, PlatformType platform) + private ProcessStartInfo GetProcessStartInfo(string executablePath, string mainExecutableArgs, PlatformType platform) { + if (mainExecutableArgs == null) + { + mainExecutableArgs = string.Empty; + } + string workingDir = Path.GetDirectoryName(executablePath) ?? string.Empty; switch (platform) { case PlatformType.Unknown: - throw new ArgumentException("Unknown");; + throw new ArgumentException("Unknown"); case PlatformType.Windows: return new ProcessStartInfo { FileName = executablePath, - Arguments = string.Format("+patcher-data-location \"{0}\"", _app.LocalMetaData.GetFilePath()), + Arguments = string.Format("+patcher-data-location \"{0}\" " + mainExecutableArgs, _app.LocalMetaData.GetFilePath()), WorkingDirectory = workingDir }; case PlatformType.OSX: + if (!string.IsNullOrEmpty(mainExecutableArgs)) + { + mainExecutableArgs = " --args " + mainExecutableArgs; + } + return new ProcessStartInfo { FileName = "open", - Arguments = string.Format("\"{0}\"", executablePath), + Arguments = string.Format("\"{0}\"{1}", executablePath, mainExecutableArgs), WorkingDirectory = workingDir }; case PlatformType.Linux: return new ProcessStartInfo { FileName = executablePath, + Arguments = mainExecutableArgs, WorkingDirectory = workingDir }; default: diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterConfiguration.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterConfiguration.cs index 033c2f5b..f6e084db 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterConfiguration.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterConfiguration.cs @@ -5,8 +5,6 @@ namespace PatchKit.Unity.Patcher.AppUpdater [Serializable] public class AppUpdaterConfiguration { - public bool UseTorrents; - public bool CheckConsistencyBeforeDiffUpdate; public long HashSizeThreshold = 1024 * 1024 * 1024; // in bytes diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterContentStrategy.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterContentStrategy.cs index f2b66c91..ac0188a1 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterContentStrategy.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterContentStrategy.cs @@ -1,4 +1,7 @@ -using PatchKit.Unity.Patcher.AppUpdater.Status; +using System; +using System.Diagnostics; +using PatchKit.Unity.Patcher.AppUpdater.Status; +using PatchKit.Unity.Patcher.AppData.Remote; using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; @@ -56,6 +59,8 @@ public void Update(CancellationToken cancellationToken) var uninstall = commandFactory.CreateUninstallCommand(_context); uninstall.Prepare(_status); + var resource = _context.App.RemoteData.GetContentPackageResource(latestVersionId, validateLicense.KeySecret, geolocateCommand.CountryCode); + var downloadContentPackage = commandFactory.CreateDownloadContentPackageCommand(latestVersionId, validateLicense.KeySecret, geolocateCommand.CountryCode, _context); downloadContentPackage.Prepare(_status); @@ -64,7 +69,38 @@ public void Update(CancellationToken cancellationToken) installContent.Prepare(_status); uninstall.Execute(cancellationToken); - downloadContentPackage.Execute(cancellationToken); + + var downloadStopwatch = new Stopwatch(); + var optionalParams = new PatcherStatistics.OptionalParams + { + VersionId = latestVersionId, + Size = resource.Size, + }; + + Func timedParams = () => new PatcherStatistics.OptionalParams { + VersionId = optionalParams.VersionId, + Size = optionalParams.Size, + Time = downloadStopwatch.Elapsed.Seconds, + }; + + try + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ContentDownloadStarted, optionalParams); + downloadStopwatch.Start(); + downloadContentPackage.Execute(cancellationToken); + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ContentDownloadSucceeded, timedParams()); + } + catch (OperationCanceledException) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ContentDownloadCanceled, timedParams()); + throw; + } + catch (Exception) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ContentDownloadFailed, timedParams()); + throw; + } + installContent.Execute(cancellationToken); _context.App.DownloadDirectory.Clear(); diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterDiffStrategy.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterDiffStrategy.cs index 46d411a3..5dda6372 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterDiffStrategy.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterDiffStrategy.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using PatchKit.Unity.Patcher.AppUpdater.Commands; using PatchKit.Unity.Patcher.AppUpdater.Status; +using PatchKit.Unity.Patcher.AppData.Remote; using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; @@ -10,7 +12,14 @@ public class AppUpdaterDiffStrategy: IAppUpdaterStrategy { private struct DiffCommands { - public IDownloadPackageCommand Download; + public struct Context + { + public CommandType Command; + public int VersionId; + public long Size; + } + + public Context Download; public IInstallDiffCommand Install; } @@ -70,9 +79,15 @@ public void Update(CancellationToken cancellationToken) { DiffCommands diffCommands; - diffCommands.Download = commandFactory.CreateDownloadDiffPackageCommand(i, validateLicense.KeySecret, - geolocateCommand.CountryCode, _context); - diffCommands.Download.Prepare(_status); + var resource = _context.App.RemoteData.GetDiffPackageResource(i, validateLicense.KeySecret, geolocateCommand.CountryCode); + + diffCommands.Download = new DiffCommands.Context{ + Command = commandFactory.CreateDownloadDiffPackageCommand(i, validateLicense.KeySecret, + geolocateCommand.CountryCode, _context), + VersionId = i, + Size = resource.Size, + }; + diffCommands.Download.Command.Prepare(_status); diffCommands.Install = commandFactory.CreateInstallDiffCommand(i, _context); diffCommands.Install.Prepare(_status); @@ -82,7 +97,30 @@ public void Update(CancellationToken cancellationToken) foreach (var diffCommands in diffCommandsList) { - diffCommands.Download.Execute(cancellationToken); + var optionalParams = new PatcherStatistics.OptionalParams + { + Size = diffCommands.Download.Size, + VersionId = diffCommands.Download.VersionId + }; + + try + { + + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatchDownloadStarted, optionalParams); + diffCommands.Download.Command.Execute(cancellationToken); + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatchDownloadSucceeded, optionalParams); + } + catch (OperationCanceledException) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatchDownloadCanceled, optionalParams); + throw; + } + catch (Exception) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatchDownloadFailed, optionalParams); + throw; + } + diffCommands.Install.Execute(cancellationToken); } diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterRepairStrategy.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterRepairStrategy.cs index 3708650f..1c1f9851 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterRepairStrategy.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/AppUpdaterRepairStrategy.cs @@ -6,6 +6,7 @@ using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; using PatchKit.Unity.Patcher.AppData.Local; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.AppData.Remote.Downloaders; using PatchKit.Unity.Patcher.AppUpdater.Commands; using PatchKit.Unity.Patcher.AppUpdater.Status; @@ -54,8 +55,8 @@ public void Update(CancellationToken cancellationToken) geolocateCommand.Execute(cancellationToken); var resource = _context.App.RemoteData.GetContentPackageResource( - installedVersionId, - validateLicense.KeySecret, + installedVersionId, + validateLicense.KeySecret, geolocateCommand.CountryCode); if (!resource.HasMetaUrls()) @@ -67,7 +68,7 @@ public void Update(CancellationToken cancellationToken) var downloader = new HttpDownloader(metaDestination, resource.GetMetaUrls()); downloader.Download(cancellationToken); - ICheckVersionIntegrityCommand checkVersionIntegrityCommand + ICheckVersionIntegrityCommand checkVersionIntegrityCommand = commandFactory.CreateCheckVersionIntegrityCommand(installedVersionId, _context); checkVersionIntegrityCommand.Prepare(_status); @@ -90,7 +91,7 @@ ICheckVersionIntegrityCommand checkVersionIntegrityCommand string actualFileHash = HashCalculator.ComputeFileHash(localPath); if (actualFileHash != file.Hash) { - FileOperations.Delete(localPath); + FileOperations.Delete(localPath, cancellationToken); _context.App.LocalMetaData.RegisterEntry(fileName, installedVersionId); invalidVersionIdFile.Status = FileIntegrityStatus.MissingData; } @@ -103,7 +104,7 @@ ICheckVersionIntegrityCommand checkVersionIntegrityCommand Pack1Meta.FileEntry[] brokenFiles = filesIntegrity // Filter only files with invalid size, hash or missing entirely - .Where(f => f.Status == FileIntegrityStatus.InvalidHash + .Where(f => f.Status == FileIntegrityStatus.InvalidHash || f.Status == FileIntegrityStatus.InvalidSize || f.Status == FileIntegrityStatus.MissingData) // Map to file entires from meta @@ -118,7 +119,7 @@ ICheckVersionIntegrityCommand checkVersionIntegrityCommand return; } _logger.LogDebug(string.Format("Broken files count: {0}", brokenFiles.Length)); - + IRepairFilesCommand repairCommand = commandFactory.CreateRepairFilesCommand( installedVersionId, _context, diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/AppUpdaterCommandFactory.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/AppUpdaterCommandFactory.cs index f110d446..d8cd9c49 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/AppUpdaterCommandFactory.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/AppUpdaterCommandFactory.cs @@ -21,9 +21,7 @@ public IDownloadPackageCommand CreateDownloadContentPackageCommand(int versionId appDownloadDirectory.PrepareForWriting(); - bool useTorrents = context.App.RemoteMetaData.GetAppInfo().PublishMethod == "any" && - context.Configuration.UseTorrents; - return new DownloadPackageCommand(resource, destinationFilePath, destinationMetaPath, useTorrents); + return new DownloadPackageCommand(resource, destinationFilePath, destinationMetaPath); } public IDownloadPackageCommand CreateDownloadDiffPackageCommand(int versionId, string keySecret, @@ -37,9 +35,7 @@ public IDownloadPackageCommand CreateDownloadDiffPackageCommand(int versionId, s appDownloadDirectory.PrepareForWriting(); - bool useTorrents = context.App.RemoteMetaData.GetAppInfo().PublishMethod == "any" && - context.Configuration.UseTorrents; - return new DownloadPackageCommand(resource, destinationFilePath, destinationMetaPath, useTorrents); + return new DownloadPackageCommand(resource, destinationFilePath, destinationMetaPath); } public IRepairFilesCommand CreateRepairFilesCommand(int versionId, AppUpdaterContext context, RemoteResource resource, Pack1Meta.FileEntry[] brokenFiles, Pack1Meta meta) @@ -101,8 +97,8 @@ public IUninstallCommand CreateUninstallCommand(AppUpdaterContext context) public IValidateLicenseCommand CreateValidateLicenseCommand(AppUpdaterContext context) { Assert.IsNotNull(Patcher.Instance.Data); - - return new ValidateLicenseCommand(context.LicenseDialog, context.App.RemoteMetaData, context.App.LocalMetaData, + + return new ValidateLicenseCommand(context.LicenseDialog, context.App.RemoteMetaData, context.App.LocalMetaData, new UnityCache(Patcher.Instance.Data.Value.AppSecret), PatcherLogManager.DefaultLogger, PatcherLogManager.Instance); } diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/CheckVersionIntegrityCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/CheckVersionIntegrityCommand.cs index 57efa13b..c6cccdad 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/CheckVersionIntegrityCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/CheckVersionIntegrityCommand.cs @@ -1,4 +1,7 @@ -using System.IO; +using System; +using System.IO; +using System.Linq; +using System.Diagnostics; using PatchKit.Api.Models.Main; using PatchKit.Unity.Patcher.AppData; using PatchKit.Unity.Patcher.AppData.Local; @@ -60,6 +63,40 @@ public override void Execute(CancellationToken cancellationToken) { base.Execute(cancellationToken); + var integrityCheckStopwatch = new Stopwatch(); + var optionalParams = new PatcherStatistics.OptionalParams + { + VersionId = _versionId, + }; + + System.Func timedParams = () => new PatcherStatistics.OptionalParams { + VersionId = optionalParams.VersionId, + Time = integrityCheckStopwatch.Elapsed.Seconds, + }; + + try + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ValidationStarted, optionalParams); + ExecuteInternal(cancellationToken); + + if (Results.Files.All(integrity => integrity.Status == FileIntegrityStatus.Ok)) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ValidationSucceeded, timedParams()); + } + else + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ValidationFailed, timedParams()); + } + } + catch (System.OperationCanceledException) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.ValidationCanceled, timedParams()); + throw; + } + } + + private void ExecuteInternal(CancellationToken cancellationToken) + { DebugLogger.Log("Checking version integrity."); _status.IsActive.Value = true; @@ -80,20 +117,32 @@ public override void Execute(CancellationToken cancellationToken) private FileIntegrity CheckFile(AppContentSummaryFile file) { + Action onVerificationFailed = () => + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.FileVerificationFailed, new PatcherStatistics.OptionalParams + { + FileName = file.Path, + Size = file.Size, + }); + }; + string localPath = _localDirectory.Path.PathCombine(file.Path); if (!File.Exists(localPath)) { + onVerificationFailed(); return new FileIntegrity(file.Path, FileIntegrityStatus.MissingData); } if (!_localMetaData.IsEntryRegistered(file.Path)) { + onVerificationFailed(); return new FileIntegrity(file.Path, FileIntegrityStatus.MissingMetaData); } int actualVersionId = _localMetaData.GetEntryVersionId(file.Path); if (actualVersionId != _versionId) { + onVerificationFailed(); return FileIntegrity.InvalidVersion(_versionId, actualVersionId, file.Path); } @@ -102,6 +151,7 @@ private FileIntegrity CheckFile(AppContentSummaryFile file) long actualSize = new FileInfo(localPath).Length; if (actualSize != file.Size) { + onVerificationFailed(); return FileIntegrity.InvalidSize(file.Size, actualSize, file.Path); } } @@ -111,6 +161,7 @@ private FileIntegrity CheckFile(AppContentSummaryFile file) string actualFileHash = HashCalculator.ComputeFileHash(localPath); if (actualFileHash != file.Hash) { + onVerificationFailed(); return FileIntegrity.InvalidHash(file.Hash, actualFileHash, file.Path); } } diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/DownloadPackageCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/DownloadPackageCommand.cs index 0528242b..c4ed4bd9 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/DownloadPackageCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/DownloadPackageCommand.cs @@ -1,7 +1,10 @@ -using PatchKit.Unity.Patcher.AppData.Remote; +using System; +using System.IO; +using PatchKit.Unity.Patcher.AppData.Remote; using PatchKit.Unity.Patcher.AppUpdater.Status; using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Debug; +using UniRx; namespace PatchKit.Unity.Patcher.AppUpdater.Commands { @@ -12,33 +15,41 @@ public class DownloadPackageCommand : BaseAppUpdaterCommand, IDownloadPackageCom private readonly RemoteResource _resource; private readonly string _destinationPackagePath; private readonly string _destinationMetaPath; - private readonly bool _useTorrents; private DownloadStatus _status; public DownloadPackageCommand(RemoteResource resource, string destinationPackagePath, - string destinationMetaPath, bool useTorrents) + string destinationMetaPath) { Checks.ArgumentValidRemoteResource(resource, "resource"); - Checks.ArgumentNotNullOrEmpty(destinationPackagePath, "destinationPackagePath"); - Checks.ParentDirectoryExists(destinationPackagePath); + + if (string.IsNullOrEmpty(destinationPackagePath)) + { + throw new ArgumentException(destinationPackagePath, "destinationPackagePath"); + } + + if (!Directory.Exists(Path.GetDirectoryName(destinationPackagePath))) + { + throw new ArgumentException("Parent directory doesn't exist.", "destinationPackagePath"); + } DebugLogger.LogConstructor(); DebugLogger.LogVariable(resource, "resource"); DebugLogger.LogVariable(destinationPackagePath, "destinationPackagePath"); - DebugLogger.LogVariable(useTorrents, "useTorrents"); _resource = resource; _destinationPackagePath = destinationPackagePath; _destinationMetaPath = destinationMetaPath; - _useTorrents = useTorrents; } public override void Prepare(UpdaterStatus status) { base.Prepare(status); - Checks.ArgumentNotNull(status, "statusMonitor"); + if (status == null) + { + throw new ArgumentNullException("status"); + } DebugLogger.Log("Preparing package download."); @@ -50,7 +61,7 @@ public override void Prepare(UpdaterStatus status) status.RegisterOperation(_status); } - public override void Execute(CancellationToken cancellationToken) + public override void Execute(Cancellation.CancellationToken cancellationToken) { base.Execute(cancellationToken); @@ -59,14 +70,17 @@ public override void Execute(CancellationToken cancellationToken) _status.IsActive.Value = true; _status.TotalBytes.Value = _resource.Size; - var downloader = new RemoteResourceDownloader(_destinationPackagePath, _destinationMetaPath, _resource, - _useTorrents); + var downloader = new RemoteResourceDownloader(_destinationPackagePath, _destinationMetaPath, _resource); downloader.DownloadProgressChanged += bytes => { _status.Bytes.Value = bytes; }; - downloader.Download(cancellationToken); + using (_status.BytesPerSecond.Subscribe(bps => + _status.Description.Value = bps > 0.01 ? "Downloading package..." : "Stalled...")) + { + downloader.Download(cancellationToken); + } _status.IsActive.Value = false; } } -} \ No newline at end of file +} diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallContentCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallContentCommand.cs index 780ad8e6..d5be6f0f 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallContentCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallContentCommand.cs @@ -1,6 +1,7 @@ using System.IO; using PatchKit.Api.Models.Main; using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.AppData.Local; using PatchKit.Unity.Patcher.AppUpdater.Status; using PatchKit.Unity.Patcher.Cancellation; @@ -93,7 +94,7 @@ public override void Execute(CancellationToken cancellationToken) } DebugLogger.Log("Installing content."); - + TemporaryDirectory.ExecuteIn(_packagePath + ".temp_unpack_" + Path.GetRandomFileName(), (packageDir) => { DebugLogger.LogVariable(packageDir.Path, "packageDirPath"); @@ -131,7 +132,7 @@ public override void Execute(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - InstallFile(_versionContentSummary.Files[i].Path, packageDir.Path, usedSuffix); + InstallFile(_versionContentSummary.Files[i].Path, packageDir.Path, usedSuffix, cancellationToken); _copyFilesStatus.Progress.Value = (i + 1) / (double) _versionContentSummary.Files.Length; _copyFilesStatus.Description.Value = string.Format("Installing ({0}/{1})...", i + 1, _versionContentSummary.Files.Length); @@ -158,7 +159,7 @@ private IUnarchiver CreateUnrachiver(string destinationDir, out string usedSuffi } } - private void InstallFile(string fileName, string packageDirPath, string suffix) + private void InstallFile(string fileName, string packageDirPath, string suffix, CancellationToken cancellationToken) { DebugLogger.Log(string.Format("Installing file {0}", fileName+suffix)); @@ -170,15 +171,15 @@ private void InstallFile(string fileName, string packageDirPath, string suffix) } string destinationFilePath = _localData.Path.PathCombine(fileName); - DirectoryOperations.CreateParentDirectory(destinationFilePath); + DirectoryOperations.CreateParentDirectory(destinationFilePath, cancellationToken); if (File.Exists(destinationFilePath)) { DebugLogger.LogFormat("Destination file {0} already exists, removing it.", destinationFilePath); - FileOperations.Delete(destinationFilePath); + FileOperations.Delete(destinationFilePath, cancellationToken); } - - FileOperations.Move(sourceFilePath, destinationFilePath); + + FileOperations.Move(sourceFilePath, destinationFilePath, cancellationToken); _localMetaData.RegisterEntry(fileName, _versionId); } } diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallDiffCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallDiffCommand.cs index ae5d6bed..e2ac40bf 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallDiffCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/InstallDiffCommand.cs @@ -6,6 +6,7 @@ using PatchKit.Api.Models.Main; using PatchKit.Logging; using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.AppData.Local; using PatchKit.Unity.Patcher.AppData.Remote; using PatchKit.Unity.Patcher.AppUpdater.Status; @@ -172,7 +173,7 @@ public override void Execute(CancellationToken cancellationToken) ProcessModifiedFiles(packageDir.Path, usedSuffix, tempDiffDir, cancellationToken); }); - DeleteEmptyMacAppDirectories(); + DeleteEmptyMacAppDirectories(cancellationToken); }); _logger.LogDebug("Diff installed."); @@ -275,7 +276,7 @@ private void ProcessRemovedFiles(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - RemoveFile(fileName); + RemoveFile(fileName, cancellationToken); counter++; _removeFilesStatusReporter.Progress.Value = counter / (double) _diffSummary.RemovedFiles.Length; @@ -286,7 +287,7 @@ private void ProcessRemovedFiles(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - RemoveDir(dirName); + RemoveDir(dirName, cancellationToken); counter++; _removeFilesStatusReporter.Progress.Value = counter / (double) _diffSummary.RemovedFiles.Length; @@ -299,7 +300,7 @@ private void ProcessRemovedFiles(CancellationToken cancellationToken) _logger.LogDebug("Diff removed files processed."); } - private void RemoveFile(string fileName) + private void RemoveFile(string fileName, CancellationToken cancellationToken) { _logger.LogDebug(string.Format("Processing remove file entry {0}", fileName)); @@ -310,7 +311,7 @@ private void RemoveFile(string fileName) if (File.Exists(filePath)) { _logger.LogDebug("File exists. Deleting it..."); - File.Delete(filePath); + FileOperations.Delete(filePath, cancellationToken); _logger.LogDebug("File deleted."); } else @@ -323,7 +324,7 @@ private void RemoveFile(string fileName) _logger.LogDebug("Remove file entry processed."); } - private void RemoveDir(string dirName) + private void RemoveDir(string dirName, CancellationToken cancellationToken) { _logger.LogDebug(string.Format("Processing remove directory entry {0}", dirName)); @@ -338,7 +339,7 @@ private void RemoveDir(string dirName) if (IsDirectoryEmpty(dirPath)) { _logger.LogDebug("Directory is empty. Deleting it..."); - Directory.Delete(dirPath); + DirectoryOperations.Delete(dirPath, cancellationToken); _logger.LogDebug("Directory deleted."); } else @@ -377,11 +378,11 @@ private void ProcessAddedFiles(string packageDirPath, string suffix, if (entryName.EndsWith("/")) { - AddDirectory(entryName); + AddDirectory(entryName, cancellationToken); } else { - AddFile(entryName, packageDirPath, suffix); + AddFile(entryName, packageDirPath, suffix, cancellationToken); } _addFilesStatusReporter.Progress.Value = (i + 1) / (double) _diffSummary.AddedFiles.Length; @@ -394,7 +395,7 @@ private void ProcessAddedFiles(string packageDirPath, string suffix, _logger.LogDebug("Diff added files processed."); } - private void AddDirectory(string dirName) + private void AddDirectory(string dirName, CancellationToken cancellationToken) { _logger.LogDebug(string.Format("Processing add directory entry {0}", dirName)); @@ -402,13 +403,13 @@ private void AddDirectory(string dirName) _logger.LogTrace("dirPath = " + dirPath); _logger.LogDebug("Creating directory in local data..."); - Directory.CreateDirectory(dirPath); + DirectoryOperations.CreateDirectory(dirPath, cancellationToken); _logger.LogDebug("Directory created."); _logger.LogDebug("Add directory entry processed."); } - private void AddFile(string fileName, string packageDirPath, string suffix) + private void AddFile(string fileName, string packageDirPath, string suffix, CancellationToken cancellationToken) { _logger.LogDebug(string.Format("Processing add file entry {0}", fileName)); @@ -428,11 +429,11 @@ private void AddFile(string fileName, string packageDirPath, string suffix) _logger.LogTrace("fileParentDirPath = " + fileParentDirPath); //TODO: Assert that fileParentDirPath is not null // ReSharper disable once AssignNullToNotNullAttribute - Directory.CreateDirectory(fileParentDirPath); + DirectoryOperations.CreateDirectory(fileParentDirPath, cancellationToken); _logger.LogDebug("File parent directories created in local data."); _logger.LogDebug("Copying file to local data (overwriting if needed)..."); - File.Copy(sourceFilePath, filePath, true); + FileOperations.Copy(sourceFilePath, filePath, true, cancellationToken); _logger.LogDebug("File copied to local data."); _localMetaData.RegisterEntry(fileName, _versionId); @@ -456,7 +457,7 @@ private void ProcessModifiedFiles(string packageDirPath, string suffix, Temporar if (!entryName.EndsWith("/")) { - PatchFile(entryName, packageDirPath, suffix, tempDiffDir); + PatchFile(entryName, packageDirPath, suffix, tempDiffDir, cancellationToken); } _modifiedFilesStatusReporter.Progress.Value = (i + 1) / (double) _diffSummary.ModifiedFiles.Length; @@ -470,7 +471,9 @@ private void ProcessModifiedFiles(string packageDirPath, string suffix, Temporar } - private void PatchFile(string fileName, string packageDirPath, string suffix, TemporaryDirectory tempDiffDir) + private void PatchFile( + string fileName, string packageDirPath, string suffix, + TemporaryDirectory tempDiffDir, CancellationToken cancellationToken) { _logger.LogDebug(string.Format("Processing patch file entry {0}", fileName)); @@ -514,10 +517,10 @@ private void PatchFile(string fileName, string packageDirPath, string suffix, Te filePatcher.Patch(); _logger.LogDebug("New file generated. Deleting old file in local data..."); - File.Delete(filePath); + FileOperations.Delete(filePath, cancellationToken); _logger.LogDebug("Old file deleted. Moving new file to local data..."); - File.Move(newFilePath, filePath); + FileOperations.Move(newFilePath, filePath, cancellationToken); _logger.LogDebug("New file moved."); } @@ -541,7 +544,7 @@ private bool IsPatchingFileContentNecessary(string fileName) } // TODO: Temporary solution for situation when .app directory is not deleted - private void DeleteEmptyMacAppDirectories() + private void DeleteEmptyMacAppDirectories(CancellationToken cancellationToken) { if (!Platform.IsOSX()) { @@ -553,7 +556,7 @@ private void DeleteEmptyMacAppDirectories() foreach (var dir in FindEmptyMacAppDirectories()) { _logger.LogDebug(string.Format("Deleting {0}", dir)); - Directory.Delete(dir, true); + DirectoryOperations.Delete(dir, cancellationToken, true); _logger.LogDebug("Directory deleted."); } diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/RepairFilesCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/RepairFilesCommand.cs index 50b7cd36..adcb89c9 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/RepairFilesCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/RepairFilesCommand.cs @@ -4,6 +4,7 @@ using PatchKit.Logging; using PatchKit.Network; using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.AppData.Local; using PatchKit.Unity.Patcher.AppData.Remote; using PatchKit.Unity.Patcher.AppData.Remote.Downloaders; @@ -76,7 +77,7 @@ public override void Execute(CancellationToken cancellationToken) if (!Directory.Exists(unarchivePath)) { - DirectoryOperations.CreateDirectory(unarchivePath); + DirectoryOperations.CreateDirectory(unarchivePath, cancellationToken); } var downloader = new ChunkedHttpDownloader(packagePath, _resource.ResourceUrls, _resource.ChunksData, _resource.Size); @@ -122,7 +123,7 @@ public override void Execute(CancellationToken cancellationToken) unarchiver.UnarchiveSingleFile(entry, cancellationToken); - EmplaceFile(Path.Combine(unarchivePath, entry.Name + _unpackingSuffix), Path.Combine(_localData.Path, entry.Name)); + EmplaceFile(Path.Combine(unarchivePath, entry.Name + _unpackingSuffix), Path.Combine(_localData.Path, entry.Name), cancellationToken); repairStatus.IsActive.Value = false; }); @@ -157,7 +158,7 @@ public override void Prepare(UpdaterStatus status) _localData.PrepareForWriting(); } - private void EmplaceFile(string source, string target) + private void EmplaceFile(string source, string target, CancellationToken cancellationToken) { _logger.LogDebug(string.Format("Installing file {0} into {1}", source, target)); @@ -166,14 +167,14 @@ private void EmplaceFile(string source, string target) throw new Exception(string.Format("Source file {0} doesn't exist.", source)); } - DirectoryOperations.CreateParentDirectory(target); + DirectoryOperations.CreateParentDirectory(target, cancellationToken); if (File.Exists(target)) { - FileOperations.Delete(target); + FileOperations.Delete(target, cancellationToken); } - FileOperations.Move(source, target); + FileOperations.Move(source, target, cancellationToken); } } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/UninstallCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/UninstallCommand.cs index 70ae3bc8..edde1bf3 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/UninstallCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/UninstallCommand.cs @@ -1,6 +1,7 @@ using System.IO; using System.Linq; using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.AppData.Local; using PatchKit.Unity.Patcher.AppUpdater.Status; using PatchKit.Unity.Patcher.Cancellation; @@ -70,7 +71,7 @@ public override void Execute(CancellationToken cancellationToken) if (File.Exists(filePath)) { - FileOperations.Delete(filePath); + FileOperations.Delete(filePath, cancellationToken); } _localMetaData.UnregisterEntry(fileName); @@ -98,7 +99,7 @@ public override void Execute(CancellationToken cancellationToken) { if (DirectoryOperations.IsDirectoryEmpty(parentDirPath)) { - DirectoryOperations.Delete(parentDirPath, false); + DirectoryOperations.Delete(parentDirPath, cancellationToken, false); } else { diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/ValidateLicenseCommand.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/ValidateLicenseCommand.cs index ba0691b5..aeac762c 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/ValidateLicenseCommand.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Commands/ValidateLicenseCommand.cs @@ -20,7 +20,6 @@ public class ValidateLicenseCommand : BaseAppUpdaterCommand, IValidateLicenseCom [NotNull] private readonly ILocalMetaData _localMetaData; [NotNull] private readonly ICache _cache; [NotNull] private readonly ILogger _logger; - [NotNull] private readonly IIssueReporter _issueReporter; public ValidateLicenseCommand([NotNull] ILicenseDialog licenseDialog, [NotNull] IRemoteMetaData remoteMetaData, [NotNull] ILocalMetaData localMetaData, [NotNull] ICache cache, [NotNull] ILogger logger, [NotNull] IIssueReporter issueReporter) @@ -37,13 +36,14 @@ public ValidateLicenseCommand([NotNull] ILicenseDialog licenseDialog, [NotNull] _localMetaData = localMetaData; _cache = cache; _logger = logger; - _issueReporter = issueReporter; } public override void Execute(CancellationToken cancellationToken) { try { + PatcherStatistics.TryDispatchSendEvent(PatcherStatistics.Event.LicenseKeyVerificationStarted); + _logger.LogDebug("Validating license..."); base.Execute(cancellationToken); @@ -82,6 +82,7 @@ public override void Execute(CancellationToken cancellationToken) KeySecret = _remoteMetaData.GetKeySecret(key, cachedKeySecret); _logger.LogDebug("License has been validated!"); + PatcherStatistics.TryDispatchSendEvent(PatcherStatistics.Event.LicenseKeyVerificationSucceeded); _logger.LogTrace("KeySecret = " + KeySecret); @@ -95,7 +96,11 @@ public override void Execute(CancellationToken cancellationToken) "Key validation failed due to server or API error. Checking if error can be recognized and displayed to user...", apiResponseException); - if (!TryToHandleApiErrors(apiResponseException.StatusCode, ref messageType, isUsingCachedKey)) + if (TryToHandleApiErrors(apiResponseException.StatusCode, ref messageType, isUsingCachedKey)) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.LicenseKeyVerificationFailed); + } + else { throw; } diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadSpeedCalculator.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadSpeedCalculator.cs index ded4f072..5492517f 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadSpeedCalculator.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadSpeedCalculator.cs @@ -15,7 +15,7 @@ private struct Sample public DateTime AddTime; } - private static readonly TimeSpan SampleLifeTime = TimeSpan.FromSeconds(10.0); + private static readonly TimeSpan SampleLifeTime = TimeSpan.FromSeconds(5.0); private static readonly TimeSpan MinimumDelayBetweenSamples = TimeSpan.FromSeconds(1.0); @@ -37,9 +37,9 @@ public void Restart(DateTime time) _samples.Clear(); } - public void AddSample(long bytes, DateTime time) + public void AddSample(long? bytes, DateTime time) { - if (_lastBytes > bytes) + if (bytes.HasValue && _lastBytes > bytes) { Restart(time); } @@ -53,17 +53,28 @@ public void AddSample(long bytes, DateTime time) CleanOldSamples(time); - _samples.Add(new Sample + if (bytes.HasValue) { - Bytes = bytes - _lastBytes, - Duration = duration, - AddTime = time - }); + _samples.Add(new Sample + { + Bytes = bytes.Value - _lastBytes, + Duration = duration, + AddTime = time + }); + + _lastBytes = bytes.Value; + } - _lastBytes = bytes; _lastTime = time; } + public double Calculate(long? bytes, DateTime time) + { + AddSample(bytes, DateTime.Now); + + return BytesPerSecond; + } + public double BytesPerSecond { get diff --git a/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadStatus.cs b/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadStatus.cs index 2872d961..efc7aa86 100644 --- a/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadStatus.cs +++ b/Assets/PatchKit Patcher/Scripts/AppUpdater/Status/DownloadStatus.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using UniRx; namespace PatchKit.Unity.Patcher.AppUpdater.Status @@ -11,11 +12,18 @@ public class DownloadStatus : IReadOnlyDownloadStatus public ReactiveProperty IsActive { get; private set; } public ReactiveProperty Description { get; private set; } + public IReadOnlyReactiveProperty Progress { get; private set; } + + public IReadOnlyReactiveProperty BytesPerSecond { get; private set; } + private readonly DownloadSpeedCalculator _downloadSpeedCalculator = new DownloadSpeedCalculator(); - private readonly ReactiveProperty _bytesPerSecond = - new ReactiveProperty(); + private struct ByteSample + { + public long? Bytes; + public DateTime Timestamp; + } public DownloadStatus() { @@ -32,11 +40,23 @@ public DownloadStatus() _downloadSpeedCalculator.Restart(DateTime.Now); }); - Bytes.Subscribe(b => - { - _downloadSpeedCalculator.AddSample(b, DateTime.Now); - _bytesPerSecond.Value = _downloadSpeedCalculator.BytesPerSecond; + var timedBytes = Bytes.Select(b => new ByteSample{ + Bytes = b, + Timestamp = DateTime.Now }); + + var interval = Observable + .Interval(TimeSpan.FromSeconds(1)) + .Select(_ => new ByteSample{ + Bytes = Bytes.Value, + Timestamp = DateTime.Now + }); + + var updateStream = timedBytes.Merge(interval); + + BytesPerSecond = updateStream + .Select(b => _downloadSpeedCalculator.Calculate(b.Bytes, b.Timestamp)) + .ToReadOnlyReactiveProperty(); } IReadOnlyReactiveProperty IReadOnlyDownloadStatus.Bytes @@ -49,13 +69,6 @@ IReadOnlyReactiveProperty IReadOnlyDownloadStatus.TotalBytes get { return TotalBytes; } } - public IReadOnlyReactiveProperty BytesPerSecond - { - get { return _bytesPerSecond; } - } - - public IReadOnlyReactiveProperty Progress { get; private set; } - IReadOnlyReactiveProperty IReadOnlyOperationStatus.Weight { get { return Weight; } diff --git a/Assets/PatchKit Patcher/Scripts/Background.cs b/Assets/PatchKit Patcher/Scripts/Background.cs index a83b3f88..1e5690ee 100644 --- a/Assets/PatchKit Patcher/Scripts/Background.cs +++ b/Assets/PatchKit Patcher/Scripts/Background.cs @@ -12,6 +12,9 @@ using System; using System.IO; using System.Collections; +using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using CancellationToken = PatchKit.Unity.Patcher.Cancellation.CancellationToken; public class Background : MonoBehaviour { @@ -83,11 +86,12 @@ private void Start() Assert.IsNotNull(NewImage); Assert.IsNotNull(OldImage); - var patcherData = patcher.Data + patcher.Data .SkipWhile(data => string.IsNullOrEmpty(data.AppSecret)) .First() .ObserveOnMainThread() .Subscribe(Initialize); + //TODO: Dispose subscription } private void Initialize(PatcherData data) @@ -102,10 +106,12 @@ private void Initialize(PatcherData data) var patcher = Patcher.Instance; - var appInfo = patcher.AppInfo + patcher.AppInfo .SkipWhile(info => info.Id == default(int)) - .Select(info => new Data{ - BannerData = new PatcherBannerData{ + .Select(info => new Data + { + BannerData = new PatcherBannerData + { ImageUrl = info.PatcherBannerImage, Dimensions = info.PatcherBannerImageDimensions, ModificationDate = info.PatcherBannerImageUpdatedAt @@ -114,6 +120,7 @@ private void Initialize(PatcherData data) }) .ObserveOnMainThread() .Subscribe(OnBannerDataUpdate); + //TODO: Dispose subscription } private void OnBannerDataUpdate(Data data) @@ -193,7 +200,7 @@ private void ClearCachedBanner() return; } - File.Delete(CachedBannerPath); + FileOperations.Delete(CachedBannerPath, CancellationToken.Empty); CachedBannerPath = ""; } diff --git a/Assets/PatchKit Patcher/Scripts/Debug/Checks.cs b/Assets/PatchKit Patcher/Scripts/Debug/Checks.cs index 6fc44116..a0335d46 100644 --- a/Assets/PatchKit Patcher/Scripts/Debug/Checks.cs +++ b/Assets/PatchKit Patcher/Scripts/Debug/Checks.cs @@ -4,7 +4,8 @@ namespace PatchKit.Unity.Patcher.Debug { - [Obsolete("Do custom checks and manual exception throwing instead.")] + //TODO: Surround it with scripting define that wouldn't display warnings in Unity console + //[Obsolete("Do custom checks and manual exception throwing instead.")] public class Checks { protected delegate void ValidationFailedHandler(string message); @@ -32,10 +33,6 @@ protected static void ValidRemoteResource(RemoteResource resource, ValidationFai { validationFailed("Resource urls are null or empty."); } - else if (resource.TorrentUrls == null || resource.TorrentUrls.Length == 0) - { - validationFailed("Resource torrent urls are null or empty."); - } } protected static void MoreThanZero(T value, ValidationFailedHandler validationFailed) where T : IComparable diff --git a/Assets/PatchKit Patcher/Scripts/Debug/EnvironmentInfo.cs b/Assets/PatchKit Patcher/Scripts/Debug/EnvironmentInfo.cs index ffb95911..aeecc26d 100644 --- a/Assets/PatchKit Patcher/Scripts/Debug/EnvironmentInfo.cs +++ b/Assets/PatchKit Patcher/Scripts/Debug/EnvironmentInfo.cs @@ -14,6 +14,11 @@ public static string GetSystemVersion() return Environment.OSVersion.ToString(); } + public static string GetSystemInformation() + { + return UnityEngine.SystemInfo.operatingSystem; + } + public static bool TryReadEnvironmentVariable(string argumentName, out string value) { value = Environment.GetEnvironmentVariable(argumentName); diff --git a/Assets/PatchKit Patcher/Scripts/Debug/PatcherLogSentryRegistry.cs b/Assets/PatchKit Patcher/Scripts/Debug/PatcherLogSentryRegistry.cs index 078fe552..5c633e21 100644 --- a/Assets/PatchKit Patcher/Scripts/Debug/PatcherLogSentryRegistry.cs +++ b/Assets/PatchKit Patcher/Scripts/Debug/PatcherLogSentryRegistry.cs @@ -10,21 +10,21 @@ public class PatcherLogSentryRegistry { public RavenClient RavenClient { - get + get { return _ravenClient; } } - + private readonly RavenClient _ravenClient; - public static readonly string RavenClientId = "https://cb13d9a4a32f456c8411c79c6ad7be9d:90ba86762829401e925a9e5c4233100c@sentry.io/175617"; + public static readonly string RavenClientId = "https://cb13d9a4a32f456c8411c79c6ad7be9d:90ba86762829401e925a9e5c4233100c@sentry.io/175617"; public PatcherLogSentryRegistry() { - ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true; - - _ravenClient = new RavenClient(RavenClientId); + ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true; + + _ravenClient = new RavenClient(RavenClientId); } public void RegisterWithException(Issue issue, string logFileGuid) @@ -40,7 +40,7 @@ public void RegisterWithException(Issue issue, string logFileGuid) AddDataToSentryEvent(sentryEvent, logFileGuid); _ravenClient.Capture(sentryEvent); } - + public void RegisterWithException(Exception exception, string logFileGuid) { RegisterWithException(new Issue() @@ -71,9 +71,10 @@ public static void AddDataToSentryEvent(SentryEvent sentryEvent, string logFileG sentryEvent.Exception.Data.Add("remote-version", patcher.RemoteVersionId.Value.ToString()); } + sentryEvent.Tags.Add("system-info", EnvironmentInfo.GetSystemInformation()); sentryEvent.Tags.Add("patcher-version", Version.Value); } - } + } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/Debug/PatcherTemporaryLogFile.cs b/Assets/PatchKit Patcher/Scripts/Debug/PatcherTemporaryLogFile.cs index d0f03602..104b3e9c 100644 --- a/Assets/PatchKit Patcher/Scripts/Debug/PatcherTemporaryLogFile.cs +++ b/Assets/PatchKit Patcher/Scripts/Debug/PatcherTemporaryLogFile.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; namespace PatchKit.Unity.Patcher.Debug { @@ -65,7 +68,7 @@ private void ReleaseUnmanagedResources() { if (File.Exists(FilePath)) { - File.Delete(FilePath); + FileOperations.Delete(FilePath, CancellationToken.Empty); } } catch diff --git a/Assets/PatchKit Patcher/Scripts/InputArgumentsPatcherDataReader.cs b/Assets/PatchKit Patcher/Scripts/InputArgumentsPatcherDataReader.cs index 09e15676..797a131a 100644 --- a/Assets/PatchKit Patcher/Scripts/InputArgumentsPatcherDataReader.cs +++ b/Assets/PatchKit Patcher/Scripts/InputArgumentsPatcherDataReader.cs @@ -82,6 +82,19 @@ public PatcherData Read() DebugLogger.LogWarning("Lock file not provided."); } + if (HasArgument("--online")) + { + data.IsOnline = true; + } + else if (HasArgument("--offline")) + { + data.IsOnline = false; + } + else + { + data.IsOnline = null; + } + return data; } diff --git a/Assets/PatchKit Patcher/Scripts/Patcher.cs b/Assets/PatchKit Patcher/Scripts/Patcher.cs index 57db4529..715f0c91 100644 --- a/Assets/PatchKit Patcher/Scripts/Patcher.cs +++ b/Assets/PatchKit Patcher/Scripts/Patcher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Diagnostics; using System.Linq; @@ -15,6 +15,8 @@ using CancellationToken = PatchKit.Unity.Patcher.Cancellation.CancellationToken; using System.IO; using PatchKit.Network; +using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; using PatchKit.Unity.Patcher.AppUpdater.Status; namespace PatchKit.Unity.Patcher @@ -60,7 +62,7 @@ public static Patcher Instance private Thread _thread; - private bool _isThreadBeingKilled; + private bool _isForceQuitting; private App _app; @@ -78,6 +80,9 @@ public static Patcher Instance private bool _hasAutomaticallyStartedApp; + private bool _wasUpdateSuccessfulOrNotNecessary = false; + private bool _hasGameBeenStarted = false; + private FileStream _lockFileStream; private CancellationTokenSource _updateAppCancellationTokenSource; @@ -111,6 +116,13 @@ public IReadOnlyReactiveProperty CanStartApp get { return _canStartApp; } } + private readonly BoolReactiveProperty _isAppInstalled = new BoolReactiveProperty(false); + + public IReadOnlyReactiveProperty IsAppInstalled + { + get { return _isAppInstalled; } + } + private readonly BoolReactiveProperty _canInstallApp = new BoolReactiveProperty(false); public IReadOnlyReactiveProperty CanInstallApp @@ -188,7 +200,6 @@ public void CancelUpdateApp() public void Quit() { DebugLogger.Log("Quitting application."); - _canStartThread = false; #if UNITY_EDITOR if (Application.isEditor) @@ -198,7 +209,6 @@ public void Quit() else #endif { - CloseLockFile(); Application.Quit(); } } @@ -212,12 +222,15 @@ private void CloseLockFile() _lockFileStream.Close(); DebugLogger.Log("Deleting the lock file."); - File.Delete(_data.Value.LockFilePath); + if (File.Exists(_data.Value.LockFilePath)) + { + FileOperations.Delete(_data.Value.LockFilePath, CancellationToken.Empty); + } } } - catch + catch(Exception e) { - DebugLogger.LogWarning("Lock file closing error"); + DebugLogger.LogWarning("Lock file closing error - " + e); } } @@ -273,35 +286,44 @@ private void Update() private void OnApplicationQuit() { - if (_thread != null && _thread.IsAlive) - { - DebugLogger.Log("Cancelling application quit because patcher thread is alive."); - - Application.CancelQuit(); - - StartCoroutine(KillThread()); - } + Application.CancelQuit(); + StartCoroutine(ForceQuit()); } - private IEnumerator KillThread() + private IEnumerator ForceQuit() { - if (_isThreadBeingKilled) + if (_isForceQuitting) { yield break; } - _isThreadBeingKilled = true; + _isForceQuitting = true; + + try + { + _canStartThread = false; - DebugLogger.Log("Killing patcher thread..."); + CloseLockFile(); - yield return StartCoroutine(KillThreadInner()); + yield return StartCoroutine(KillThread()); - DebugLogger.Log("Patcher thread has been killed."); + if (_wasUpdateSuccessfulOrNotNecessary && !_hasGameBeenStarted) + { + yield return StartCoroutine(PatcherStatistics.SendEvent(PatcherStatistics.Event.PatcherSucceededClosed)); + } - _isThreadBeingKilled = false; + if (!Application.isEditor) + { + Process.GetCurrentProcess().Kill(); + } + } + finally + { + _isForceQuitting = false; + } } - private IEnumerator KillThreadInner() + private IEnumerator KillThread() { if (_thread == null) { @@ -344,6 +366,8 @@ private IEnumerator KillThreadInner() yield return null; } } + + _thread = null; } private void StartThread() @@ -411,7 +435,7 @@ private void ThreadExecution(CancellationToken cancellationToken) UnityDispatcher.Invoke(() => _app = new App(_data.Value.AppDataPath, _data.Value.AppSecret, _data.Value.OverrideLatestVersionId, _requestTimeoutCalculator)).WaitOne(); - UnityDispatcher.InvokeCoroutine(PatcherStatistics.SendEvent("patcher_started", _data.Value.AppSecret)); + PatcherStatistics.TryDispatchSendEvent(PatcherStatistics.Event.PatcherStarted); while (true) { @@ -571,6 +595,8 @@ private void ThreadWaitForUserDecision(CancellationToken cancellationToken) bool canCheckForAppUpdates = isInstalled; bool canStartApp = isInstalled; + _isAppInstalled.Value = isInstalled; + _canRepairApp.Value = false; _canInstallApp.Value = false; _canCheckForAppUpdates.Value = false; @@ -753,6 +779,8 @@ private void ThreadExecuteUserDecision(CancellationToken cancellationToken) private void ThreadDisplayError(PatcherError error, CancellationToken cancellationToken) { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatcherFailed); + try { _state.Value = PatcherState.DisplayingError; @@ -792,6 +820,9 @@ private void ThreadStartApp() appStarter.Start(); + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatcherSucceededGameStarted); + _hasGameBeenStarted = true; + UnityDispatcher.Invoke(Quit); } @@ -819,8 +850,15 @@ private void ThreadUpdateApp(bool automatically, CancellationToken cancellationT using (_updaterStatus.Take(1).Subscribe((status) => _state.Value = PatcherState.UpdatingApp)) { appUpdater.Update(_updateAppCancellationTokenSource.Token); + _wasUpdateSuccessfulOrNotNecessary = true; } } + catch (OperationCanceledException) + { + PatcherStatistics.DispatchSendEvent(PatcherStatistics.Event.PatcherCanceled); + + throw; + } finally { _state.Value = PatcherState.None; diff --git a/Assets/PatchKit Patcher/Scripts/PatcherData.cs b/Assets/PatchKit Patcher/Scripts/PatcherData.cs index e1b64db5..6ea4550a 100644 --- a/Assets/PatchKit Patcher/Scripts/PatcherData.cs +++ b/Assets/PatchKit Patcher/Scripts/PatcherData.cs @@ -12,5 +12,7 @@ public struct PatcherData public string LockFilePath; public int OverrideLatestVersionId; + + public bool? IsOnline; } } \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/PatcherSenderId.cs b/Assets/PatchKit Patcher/Scripts/PatcherSenderId.cs index ffbebe96..a35ea8c1 100644 --- a/Assets/PatchKit Patcher/Scripts/PatcherSenderId.cs +++ b/Assets/PatchKit Patcher/Scripts/PatcherSenderId.cs @@ -1,5 +1,8 @@ using System; using System.IO; +using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Utilities; namespace PatchKit.Unity.Patcher @@ -33,7 +36,7 @@ private static string GenerateOrRead() string parentDirPath = Path.GetDirectoryName(filePath); if (parentDirPath != null) { - Directory.CreateDirectory(parentDirPath); + DirectoryOperations.CreateDirectory(parentDirPath, CancellationToken.Empty); } File.WriteAllText(filePath, senderId); diff --git a/Assets/PatchKit Patcher/Scripts/PatcherStatistics.cs b/Assets/PatchKit Patcher/Scripts/PatcherStatistics.cs index 2eb8c326..fc47c8e2 100644 --- a/Assets/PatchKit Patcher/Scripts/PatcherStatistics.cs +++ b/Assets/PatchKit Patcher/Scripts/PatcherStatistics.cs @@ -1,6 +1,8 @@ using System; using System.Collections; +using System.ComponentModel; using System.Text; +using System.Threading; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PatchKit.Unity.Patcher.Debug; @@ -15,12 +17,134 @@ namespace PatchKit.Unity.Patcher { public class PatcherStatistics { - public static IEnumerator SendEvent(string eventName, string appSecret) + public enum Event + { + [Description("content_download_started")] + ContentDownloadStarted, + [Description("content_download_succeeded")] + ContentDownloadSucceeded, + [Description("content_download_canceled")] + ContentDownloadCanceled, + [Description("content_download_failed")] + ContentDownloadFailed, + + [Description("patch_download_started")] + PatchDownloadStarted, + [Description("patch_download_succeeded")] + PatchDownloadSucceeded, + [Description("patch_download_canceled")] + PatchDownloadCanceled, + [Description("patch_download_failed")] + PatchDownloadFailed, + + [Description("validation_started")] + ValidationStarted, + [Description("validation_succeeded")] + ValidationSucceeded, + [Description("validation_failed")] + ValidationFailed, + [Description("validation_canceled")] + ValidationCanceled, + + [Description("file_verification_failed")] + FileVerificationFailed, + + [Description("license_key_verification_started")] + LicenseKeyVerificationStarted, + [Description("license_key_verification_succeeded")] + LicenseKeyVerificationSucceeded, + [Description("license_key_verification_failed")] + LicenseKeyVerificationFailed, + + [Description("patcher_succeeded_closed")] + PatcherSucceededClosed, + [Description("patcher_canceled")] + PatcherCanceled, + [Description("patcher_started")] + PatcherStarted, + [Description("patcher_failed")] + PatcherFailed, + [Description("patcher_succeeded_game_started")] + PatcherSucceededGameStarted + } + + public struct OptionalParams + { + public int? VersionId; + public string FileName; + public long? Size; + public long? Time; + } + + public static bool TryDispatchSendEvent(Event ev, OptionalParams? parameters = null) + { + try + { + DispatchSendEvent(ev, parameters); + return true; + } + catch (ThreadAbortException) + { + throw; + } + catch (ThreadInterruptedException) + { + throw; + } + catch + { + return false; + } + } + + public static void DispatchSendEvent(Event ev, OptionalParams? parameters = null) + { + UnityDispatcher.InvokeCoroutine(PatcherStatistics.SendEvent(ev, parameters)); + } + + public static void DispatchSendEvent(Event ev, string appSecret, OptionalParams? parameters = null) + { + UnityDispatcher.InvokeCoroutine(PatcherStatistics.SendEvent(ev, appSecret, parameters)); + } + + public static IEnumerator SendEvent(Event ev, OptionalParams? parameters = null) + { + string appSecret = Patcher.Instance.Data.Value.AppSecret; + return SendEvent(ev, appSecret, parameters); + } + + private static string GetCustomDescription(object objEnum) + { + var field = objEnum.GetType().GetField(objEnum.ToString()); + var attributes = (DescriptionAttribute[]) field.GetCustomAttributes(typeof(DescriptionAttribute), false); + + return attributes.Length > 0 ? attributes[0].Description : objEnum.ToString(); + } + + private static string EventName(Event value) + { + return GetCustomDescription(value); + } + + private const string EventNameKey = "event_name"; + private const string SenderIdKey = "sender_id"; + private const string CallerKey = "caller"; + private const string AppSecretKey = "app_secret"; + private const string OperatingSystemFamilyKey = "operating_system_family"; + private const string OperatingSystemVersionKey = "operating_system_version"; + private const string VersionIdKey = "version_id"; + private const string TimeKey = "time"; + private const string SizeKey = "size"; + private const string FileNameKey = "file_name"; + + public static IEnumerator SendEvent(Event ev, string appSecret, OptionalParams? parameters = null) { string senderId = PatcherSenderId.Get(); string caller = string.Format("patcher_unity:{0}.{1}.{2}", Version.Major, Version.Minor, Version.Release); string operatingSystemFamily; + string eventName = EventName(ev); + switch (Platform.GetPlatformType()) { case PlatformType.Windows: @@ -40,12 +164,36 @@ public static IEnumerator SendEvent(string eventName, string appSecret) var json = new JObject(); - json["event_name"] = eventName; - json["sender_id"] = senderId; - json["caller"] = caller; - json["app_secret"] = appSecret; - json["operating_system_family"] = operatingSystemFamily; - json["operating_system_version"] = operatingSystemVersion; + json[EventNameKey] = eventName; + json[SenderIdKey] = senderId; + json[CallerKey] = caller; + json[AppSecretKey] = appSecret; + json[OperatingSystemFamilyKey] = operatingSystemFamily; + json[OperatingSystemVersionKey] = operatingSystemVersion; + + if (parameters.HasValue) + { + var v = parameters.Value; + if (v.VersionId.HasValue) + { + json[VersionIdKey] = v.VersionId.Value; + } + + if (v.Time.HasValue) + { + json[TimeKey] = v.Time.Value; + } + + if (v.Size.HasValue) + { + json[SizeKey] = v.Size.Value; + } + + if (!string.IsNullOrEmpty(v.FileName)) + { + json[FileNameKey] = v.FileName; + } + } UnityWebRequest request = new UnityWebRequest("https://stats.patchkit.net/v1/entry", "POST"); byte[] bodyRaw = Encoding.UTF8.GetBytes(json.ToString(Formatting.None)); diff --git a/Assets/PatchKit Patcher/Scripts/UI/BorderlessWindow.cs b/Assets/PatchKit Patcher/Scripts/UI/BorderlessWindow.cs index a3d3fc0a..8f200edd 100644 --- a/Assets/PatchKit Patcher/Scripts/UI/BorderlessWindow.cs +++ b/Assets/PatchKit Patcher/Scripts/UI/BorderlessWindow.cs @@ -118,9 +118,9 @@ private void Awake() { _logger = PatcherLogManager.DefaultLogger; -#if UNITY_STANDALONE_WIN && !UNITY_EDITOR EnforceCorrectScreenSize(); +#if UNITY_STANDALONE_WIN && !UNITY_EDITOR _windowRect.position = new Vector2(Screen.currentResolution.width/2.0f - Screen.width/2.0f, Screen.currentResolution.height/2.0f - Screen.height/2.0f); _windowRect.size = new Vector2(Screen.width, Screen.height); @@ -160,6 +160,10 @@ private void EnforceCorrectScreenSize() int width = int.Parse(screenResolutionText[0]); int height = int.Parse(screenResolutionText[1]); + PlayerPrefs.SetInt("Screenmanager Resolution Width", width); + PlayerPrefs.SetInt("Screenmanager Resolution Height", height); + PlayerPrefs.SetInt("Screenmanager Is Fullscreen mode", 0); + Screen.SetResolution(width, height, false); } catch (System.Exception e) @@ -224,4 +228,4 @@ public void CloseWindow() Application.Quit(); } } -} \ No newline at end of file +} diff --git a/Assets/PatchKit Patcher/Scripts/UI/DownloadSpeed.cs b/Assets/PatchKit Patcher/Scripts/UI/DownloadSpeed.cs index d1765625..4f5320c1 100644 --- a/Assets/PatchKit Patcher/Scripts/UI/DownloadSpeed.cs +++ b/Assets/PatchKit Patcher/Scripts/UI/DownloadSpeed.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using PatchKit.Unity.Patcher.AppUpdater.Status; using PatchKit.Unity.Utilities; using UniRx; @@ -23,14 +24,16 @@ private void Start() var text = downloadStatus.SelectSwitchOrDefault(status => { + var bytesPerSecond = status.BytesPerSecond; + var remainingTime = - status.Bytes.CombineLatest(status.TotalBytes, status.BytesPerSecond, + status.Bytes.CombineLatest(status.TotalBytes, bytesPerSecond, GetRemainingTime); var formattedRemainingTime = remainingTime.Select(GetFormattedRemainingTime); var formattedDownloadSpeed = - status.BytesPerSecond.CombineLatest(downloadSpeedUnit, + bytesPerSecond.CombineLatest(downloadSpeedUnit, GetFormattedDownloadSpeed); return formattedDownloadSpeed.CombineLatest(formattedRemainingTime, @@ -47,31 +50,29 @@ private static string GetStatusText(string formattedDownloadSpeed, string format private static string GetFormattedDownloadSpeed(double bytesPerSecond, string downloadSpeedUnit) { - double kbPerSecond = bytesPerSecond / 1024.0; - switch (downloadSpeedUnit) { case "kilobytes": - return FormatDownloadSpeedKilobytes(kbPerSecond); + return FormatDownloadSpeedKilobytes(bytesPerSecond); case "megabytes": - return FormatDownloadSpeedMegabytes(kbPerSecond); + return FormatDownloadSpeedMegabytes(bytesPerSecond); default: // "human_readable" and any other { - return kbPerSecond > 1024.0 - ? FormatDownloadSpeedMegabytes(kbPerSecond) - : FormatDownloadSpeedKilobytes(kbPerSecond); + return bytesPerSecond > Units.MB + ? FormatDownloadSpeedMegabytes(bytesPerSecond) + : FormatDownloadSpeedKilobytes(bytesPerSecond); } } } - private static string FormatDownloadSpeedMegabytes(double kbPerSecond) + private static string FormatDownloadSpeedMegabytes(double bytesPerSecond) { - return FormatDownloadSpeed(kbPerSecond / 1024.0) + " MB/sec."; + return FormatDownloadSpeed(bytesPerSecond / Units.MB) + " MB/sec."; } - private static string FormatDownloadSpeedKilobytes(double kbPerSecond) + private static string FormatDownloadSpeedKilobytes(double bytesPerSecond) { - return FormatDownloadSpeed(kbPerSecond) + " KB/sec."; + return FormatDownloadSpeed(bytesPerSecond / Units.KB) + " KB/sec."; } private static string FormatDownloadSpeed(double s) diff --git a/Assets/PatchKit Patcher/Scripts/UI/ProgressBar.cs b/Assets/PatchKit Patcher/Scripts/UI/ProgressBar.cs index f0d15bce..41975527 100644 --- a/Assets/PatchKit Patcher/Scripts/UI/ProgressBar.cs +++ b/Assets/PatchKit Patcher/Scripts/UI/ProgressBar.cs @@ -1,4 +1,7 @@ -using PatchKit.Unity.Utilities; +using System; +using System.Collections.Generic; +using PatchKit.Unity.Patcher.AppUpdater.Status; +using PatchKit.Unity.Utilities; using UniRx; using UnityEngine; using UnityEngine.UI; @@ -14,10 +17,13 @@ public class ProgressBar : MonoBehaviour private struct UpdateData { public double Progress; + public PatcherState State; + + public bool IsAppInstalled; } - private void SetBar(float start, float end) + private void SetProgressBar(float start, float end) { var anchorMax = Image.rectTransform.anchorMax; var anchorMin = Image.rectTransform.anchorMin; @@ -29,60 +35,110 @@ private void SetBar(float start, float end) Image.rectTransform.anchorMin = anchorMin; } - private void SetProgress(UpdateData data) + private void SetProgressBarLinear(float progress) { - _isIdle = data.State == PatcherState.Connecting; + SetProgressBar(0, progress); + } - if (data.State == PatcherState.None) - { - Text.text = ""; - SetBar(0, 0); - return; - } + private void SetProgressBarText(string text) + { + Text.text = text; + } - if (data.State == PatcherState.DisplayingError) - { - Text.text = "Error!"; - SetBar(0, 0); - return; - } + private void SetProgress(UpdateData data) + { + } - if (data.State == PatcherState.Connecting) - { - Text.text = "Connecting..."; - return; - } + private void SetIdle() + { + SetProgressBarText("Connecting..."); + _isIdle = true; + } - double progress = data.Progress; + private string FormatProgressForDisplay(double progress) + { + return string.Format("{0:0.0}", progress * 100.0) + "%"; + } - Text.text = progress >= 0.0 ? progress.ToString("0.0%") : ""; - float visualProgress = progress >= 0.0 ? (float) progress : 0.0f; + private void OnUpdate(UpdateData data) + { + _isIdle = false; - SetBar(0, visualProgress); + switch (data.State) + { + case PatcherState.LoadingPatcherData: + case PatcherState.LoadingPatcherConfiguration: + case PatcherState.Connecting: + SetIdle(); + return; + + case PatcherState.UpdatingApp: + if (data.Progress <= 0) + { + SetIdle(); + return; + } + + SetProgressBarText(FormatProgressForDisplay(data.Progress)); + SetProgressBarLinear((float) data.Progress); + break; + + case PatcherState.WaitingForUserDecision: + if (data.IsAppInstalled) + { + SetProgressBarText(FormatProgressForDisplay(1.0)); + SetProgressBarLinear(1); + } + else + { + SetProgressBarText(FormatProgressForDisplay(0.0)); + SetProgressBarLinear(0); + } + break; + + case PatcherState.DisplayingError: + SetProgressBarText("Error..."); + SetProgressBarLinear(0); + break; + + case PatcherState.StartingApp: + SetProgressBarText(FormatProgressForDisplay(1.0)); + SetProgressBarLinear(1); + break; + + case PatcherState.None: + default: + _isIdle = false; + break; + } } private void Start() { - var progress = Patcher.Instance.UpdaterStatus - .SelectSwitchOrDefault(s => s.Progress, -1.0); + var progress = Patcher.Instance.UpdaterStatus.SelectSwitchOrDefault(p => p.Progress, -1.0); Patcher.Instance.State - .CombineLatest(progress, (state, d) => new UpdateData { Progress = d, State = state }) + .CombineLatest(progress, Patcher.Instance.IsAppInstalled, + (state, progressValue, isAppInstalled) => new UpdateData { + Progress = progressValue, + State = state, + IsAppInstalled = isAppInstalled + }) .ObserveOnMainThread() - .Subscribe(SetProgress) + .Subscribe(OnUpdate) .AddTo(this); } - private bool _isIdle = false; private const float IdleBarWidth = 0.2f; private const float IdleBarSpeed = 1.2f; private float _idleProgress = -IdleBarWidth; + private void Update() { if (_isIdle) { - SetBar(_idleProgress, _idleProgress + IdleBarWidth); + SetProgressBar(_idleProgress, _idleProgress + IdleBarWidth); _idleProgress += Time.deltaTime * IdleBarSpeed; diff --git a/Assets/PatchKit Patcher/Scripts/UI/SecondaryProgressBar.cs b/Assets/PatchKit Patcher/Scripts/UI/SecondaryProgressBar.cs index 3e0786a2..c337b128 100644 --- a/Assets/PatchKit Patcher/Scripts/UI/SecondaryProgressBar.cs +++ b/Assets/PatchKit Patcher/Scripts/UI/SecondaryProgressBar.cs @@ -11,9 +11,21 @@ public class SecondaryProgressBar : MonoBehaviour public Image Image; - private void SetProgress(double progress) + private void OnProgress(double progress) { - Text.text = progress.ToString("0.0%"); + if (progress < 0) + { + SetProgress(0, ""); + } + else + { + SetProgress(progress, progress.ToString("0.0%")); + } + } + + private void SetProgress(double progress, string text) + { + Text.text = text; var anchorMax = Image.rectTransform.anchorMax; anchorMax.x = (float)progress; Image.rectTransform.anchorMax = anchorMax; @@ -22,9 +34,9 @@ private void SetProgress(double progress) private void Start() { Patcher.Instance.UpdaterStatus.SelectSwitchOrNull(s => s.LatestActiveOperation) - .SelectSwitchOrDefault(s => s.Progress, 1.0) + .SelectSwitchOrDefault(s => s.Progress, -1.0) .ObserveOnMainThread() - .Subscribe(SetProgress) + .Subscribe(OnProgress) .AddTo(this); } } diff --git a/Assets/PatchKit Patcher/Scripts/UnsupportedPlatformException.cs b/Assets/PatchKit Patcher/Scripts/UnsupportedPlatformException.cs new file mode 100644 index 00000000..1148cbba --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/UnsupportedPlatformException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace PatchKit.Unity.Patcher +{ + [Serializable] + internal class UnsupportedPlatformException: Exception + { + public UnsupportedPlatformException() + { + } + + public UnsupportedPlatformException(string message) : base(message) + { + } + + public UnsupportedPlatformException(string message, Exception innerException) : base(message, innerException) + { + } + + protected UnsupportedPlatformException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/UnsupportedPlatformException.cs.meta b/Assets/PatchKit Patcher/Scripts/UnsupportedPlatformException.cs.meta new file mode 100644 index 00000000..231d76b7 --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/UnsupportedPlatformException.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8797f98c2b294efa9043c88375e0b487 +timeCreated: 1530710726 \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/Utilities/Files.cs b/Assets/PatchKit Patcher/Scripts/Utilities/Files.cs index b7286832..aa8d4917 100644 --- a/Assets/PatchKit Patcher/Scripts/Utilities/Files.cs +++ b/Assets/PatchKit Patcher/Scripts/Utilities/Files.cs @@ -1,5 +1,8 @@ using System; using System.IO; +using PatchKit.Unity.Patcher.AppData; +using PatchKit.Unity.Patcher.AppData.FileSystem; +using PatchKit.Unity.Patcher.Cancellation; using PatchKit.Unity.Patcher.Data; namespace PatchKit.Unity.Utilities @@ -11,7 +14,7 @@ public static void CreateParents(string path) var dirName = Path.GetDirectoryName(path); if (dirName != null) { - Directory.CreateDirectory(dirName); + DirectoryOperations.CreateDirectory(dirName, CancellationToken.Empty); } } diff --git a/Assets/PatchKit Patcher/Scripts/Utilities/GZipReadWrapperStream.cs b/Assets/PatchKit Patcher/Scripts/Utilities/GZipReadWrapperStream.cs new file mode 100644 index 00000000..1b76b467 --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/Utilities/GZipReadWrapperStream.cs @@ -0,0 +1,97 @@ +using System.IO; +using System; + +namespace PatchKit.Unity.Utilities +{ + public class GZipReadWrapperStream : Stream + { + private readonly int _minimumReadSize; + + private readonly Stream _source; + + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanSeek + { + get + { + return _source.CanSeek; + } + } + + public override bool CanWrite + { + get + { + return false; + } + } + + public override long Length + { + get + { + return _source.Length; + } + } + + public override long Position + { + get + { + return _source.Position; + } + set + { + _source.Position = value; + } + } + + public override void Flush() + { + throw new System.NotSupportedException(); + } + + public GZipReadWrapperStream(Stream sourceStream) + { + _source = sourceStream; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int totalBytesRead = 0; + int bytesRead; + + do + { + bytesRead = _source.Read(buffer, offset + totalBytesRead, count - totalBytesRead); + + totalBytesRead += bytesRead; + } + while (bytesRead > 0 && totalBytesRead < count); + + return totalBytesRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _source.Seek(offset, origin); + } + + public override void SetLength(long value) + { + throw new System.NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new System.NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClientException.cs.meta b/Assets/PatchKit Patcher/Scripts/Utilities/GZipReadWrapperStream.cs.meta similarity index 76% rename from Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClientException.cs.meta rename to Assets/PatchKit Patcher/Scripts/Utilities/GZipReadWrapperStream.cs.meta index 9de6612c..96c5aaa2 100644 --- a/Assets/PatchKit Patcher/Scripts/AppData/Remote/Downloaders/TorrentClientException.cs.meta +++ b/Assets/PatchKit Patcher/Scripts/Utilities/GZipReadWrapperStream.cs.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 9db05f698197ce646a004e9fa6a6f664 -timeCreated: 1482513761 +guid: 791a515a2746d4c26b503d7388543d6a +timeCreated: 1535636427 licenseType: Free MonoImporter: serializedVersion: 2 diff --git a/Assets/PatchKit Patcher/Scripts/Utilities/Units.cs b/Assets/PatchKit Patcher/Scripts/Utilities/Units.cs new file mode 100644 index 00000000..acbe0b8a --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/Utilities/Units.cs @@ -0,0 +1,9 @@ +namespace PatchKit.Unity.Utilities +{ + public static class Units + { + public static readonly long KB = 1024; + public static readonly long MB = 1024 * KB; + public static readonly long GB = 1024 * MB; + } +} \ No newline at end of file diff --git a/Assets/PatchKit Patcher/Scripts/Utilities/Units.cs.meta b/Assets/PatchKit Patcher/Scripts/Utilities/Units.cs.meta new file mode 100644 index 00000000..6e42ef94 --- /dev/null +++ b/Assets/PatchKit Patcher/Scripts/Utilities/Units.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2fc17aca3ef39c44ea07f9e05c85d1ae +timeCreated: 1540378989 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PatchKit Patcher/Scripts/Version.cs b/Assets/PatchKit Patcher/Scripts/Version.cs index b9a7ad7d..5125bd5a 100644 --- a/Assets/PatchKit Patcher/Scripts/Version.cs +++ b/Assets/PatchKit Patcher/Scripts/Version.cs @@ -3,12 +3,16 @@ namespace PatchKit.Unity.Patcher public static class Version { public const int Major = 3; - public const int Minor = 10; - public const int Release = 3; + public const int Minor = 11; + public const int Release = 0; public static string Value { +#if PK_OFFICIAL + get { return string.Format("v{0}.{1}.{2}-official", Major, Minor, Release); } +#else get { return string.Format("v{0}.{1}.{2}", Major, Minor, Release); } +#endif } } } diff --git a/Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll b/Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll new file mode 100644 index 00000000..2f8f5db0 Binary files /dev/null and b/Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll differ diff --git a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/NDesk.Options.dll.meta b/Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll.meta similarity index 86% rename from Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/NDesk.Options.dll.meta rename to Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll.meta index 04b4cc9e..4f89eaa2 100644 --- a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options/NDesk.Options.dll.meta +++ b/Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 3a52fc2cc0f134192b7cd95b44878d4a -timeCreated: 1490992925 +guid: 9b48c8026b867454abee3d74aa936a7d +timeCreated: 1543341229 licenseType: Free PluginImporter: serializedVersion: 1 diff --git a/Assets/Plugins/Editor/JetBrains/Unity3DRider.cs b/Assets/Plugins/Editor/JetBrains/Unity3DRider.cs deleted file mode 100644 index 6c6fed02..00000000 --- a/Assets/Plugins/Editor/JetBrains/Unity3DRider.cs +++ /dev/null @@ -1,1363 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Version: 2.1.3.5034 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -using Application = UnityEngine.Application; -using Debug = UnityEngine.Debug; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using System.Text; -using System.Xml.Linq; -using System; -using UnityEditor; -using UnityEngine; - -namespace Plugins.Editor.JetBrains -{ - public class RiderAssetPostprocessor : AssetPostprocessor - { - public static void OnGeneratedCSProjectFiles() - { - if (!RiderPlugin.Enabled) - return; - var currentDirectory = Directory.GetCurrentDirectory(); - var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj"); - - foreach (var file in projectFiles) - { - UpgradeProjectFile(file); - } - - var slnFile = RiderPlugin.SlnFile; - if (string.IsNullOrEmpty(slnFile)) - return; - - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("Post-processing {0}", slnFile)); - string slnAllText = File.ReadAllText(slnFile); - const string unityProjectGuid = @"Project(""{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1}"")"; - if (!slnAllText.Contains(unityProjectGuid)) - { - string matchGUID = @"Project\(\""\{[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}\""\)"; - // Unity may put a random guid, unityProjectGuid will help VSTU recognize Rider-generated projects - slnAllText = Regex.Replace(slnAllText, matchGUID, unityProjectGuid); - } - - var lines = slnAllText.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries); - var sb = new StringBuilder(); - foreach (var line in lines) - { - if (line.StartsWith("Project(")) - { - MatchCollection mc = Regex.Matches(line, "\"([^\"]*)\""); - //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "mc[1]: "+mc[1].Value); - //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "mc[2]: "+mc[2].Value); - var to = GetFileNameWithoutExtension(mc[2].Value.Substring(1, mc[2].Value.Length-1)); // remove quotes - //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, "to:" + to); - //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, line); - var newLine = line.Substring(0, mc[1].Index + 1) + to + line.Substring(mc[1].Index + mc[1].Value.Length - 1); - sb.Append(newLine); - //RiderPlugin.Log(RiderPlugin.LoggingLevel.Info, newLine); - } - else - { - sb.Append(line); - } - sb.Append(Environment.NewLine); - } - File.WriteAllText(slnFile, sb.ToString()); - } - - private static string GetFileNameWithoutExtension(string path) - { - if (string.IsNullOrEmpty(path)) - return null; - int length; - return (length = path.LastIndexOf('.')) == -1 ? path : path.Substring(0, length); - } - - private static void UpgradeProjectFile(string projectFile) - { - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("Post-processing {0}", projectFile)); - var doc = XDocument.Load(projectFile); - var projectContentElement = doc.Root; - XNamespace xmlns = projectContentElement.Name.NamespaceName; // do not use var - - FixTargetFrameworkVersion(projectContentElement, xmlns); - FixSystemXml(projectContentElement, xmlns); - SetLangVersion(projectContentElement, xmlns); - // Unity_5_6_OR_NEWER switched to nunit 3.5 -#if UNITY_5_6_OR_NEWER - ChangeNunitReference(projectContentElement, xmlns); -#endif - -#if !UNITY_2017_1_OR_NEWER // Unity 2017.1 and later has this features by itself - SetManuallyDefinedComilingSettings(projectFile, projectContentElement, xmlns); -#endif - SetXCodeDllReference("UnityEditor.iOS.Extensions.Xcode.dll", xmlns, projectContentElement); - SetXCodeDllReference("UnityEditor.iOS.Extensions.Common.dll", xmlns, projectContentElement); - ApplyManualCompilingSettingsReferences(projectContentElement, xmlns); - doc.Save(projectFile); - } - - private static void FixSystemXml(XElement projectContentElement, XNamespace xmlns) - { - var el = projectContentElement - .Elements(xmlns+"ItemGroup") - .Elements(xmlns+"Reference") - .FirstOrDefault(a => a.Attribute("Include").Value=="System.XML"); - if (el != null) - { - el.Attribute("Include").Value = "System.Xml"; - } - } - - private static void ChangeNunitReference(XElement projectContentElement, XNamespace xmlns) - { - var el = projectContentElement - .Elements(xmlns+"ItemGroup") - .Elements(xmlns+"Reference") - .FirstOrDefault(a => a.Attribute("Include").Value=="nunit.framework"); - if (el != null) - { - var hintPath = el.Elements(xmlns + "HintPath").FirstOrDefault(); - if (hintPath != null) - { - var path = Path.GetFullPath("Library/resharper-unity-libs/nunit3.5.0/nunit.framework.dll"); - if (new FileInfo(path).Exists) - hintPath.Value = path; - } - } - } - - private static readonly string PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/mcs.rsp"); -#if !UNITY_2017_1_OR_NEWER // Unity 2017.1 and later has this features by itself - private const string UNITY_PLAYER_PROJECT_NAME = "Assembly-CSharp.csproj"; - private const string UNITY_EDITOR_PROJECT_NAME = "Assembly-CSharp-Editor.csproj"; - private const string UNITY_UNSAFE_KEYWORD = "-unsafe"; - private const string UNITY_DEFINE_KEYWORD = "-define:"; - private static readonly string PLAYER_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/smcs.rsp"); - private static readonly string EDITOR_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH = Path.GetFullPath("Assets/gmcs.rsp"); - - private static void SetManuallyDefinedComilingSettings(string projectFile, XElement projectContentElement, XNamespace xmlns) - { - string configPath = null; - - if (IsPlayerProjectFile(projectFile) || IsEditorProjectFile(projectFile)) - { - //Prefer mcs.rsp if it exists - if (File.Exists(PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH)) - { - configPath = PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; - } - else - { - if (IsPlayerProjectFile(projectFile)) - configPath = PLAYER_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; - else if (IsEditorProjectFile(projectFile)) - configPath = EDITOR_PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; - } - } - - if(!string.IsNullOrEmpty(configPath)) - ApplyManualCompilingSettings(configPath - , projectContentElement - , xmlns); - } - - private static void ApplyManualCompilingSettings(string configFilePath, XElement projectContentElement, XNamespace xmlns) - { - if (File.Exists(configFilePath)) - { - var configText = File.ReadAllText(configFilePath); - if (configText.Contains(UNITY_UNSAFE_KEYWORD)) - { - // Add AllowUnsafeBlocks to the .csproj. Unity doesn't generate it (although VSTU does). - // Strictly necessary to compile unsafe code - ApplyAllowUnsafeBlocks(projectContentElement, xmlns); - } - if (configText.Contains(UNITY_DEFINE_KEYWORD)) - { - // defines could be - // 1) -define:DEFINE1,DEFINE2 - // 2) -define:DEFINE1;DEFINE2 - // 3) -define:DEFINE1 -define:DEFINE2 - // 4) -define:DEFINE1,DEFINE2;DEFINE3 - // tested on "-define:DEF1;DEF2 -define:DEF3,DEF4;DEFFFF \n -define:DEF5" - // result: DEF1, DEF2, DEF3, DEF4, DEFFFF, DEF5 - - var definesList = new List(); - var compileFlags = configText.Split(' ', '\n'); - foreach (var flag in compileFlags) - { - var f = flag.Trim(); - if (f.Contains(UNITY_DEFINE_KEYWORD)) - { - var defineEndPos = f.IndexOf(UNITY_DEFINE_KEYWORD) + UNITY_DEFINE_KEYWORD.Length; - var definesSubString = f.Substring(defineEndPos,f.Length - defineEndPos); - definesSubString = definesSubString.Replace(";", ","); - definesList.AddRange(definesSubString.Split(',')); - } - } - - ApplyCustomDefines(definesList.ToArray(), projectContentElement, xmlns); - } - } - } - - private static void ApplyCustomDefines(string[] customDefines, XElement projectContentElement, XNamespace xmlns) - { - var definesString = string.Join(";", customDefines); - - var DefineConstants = projectContentElement - .Elements(xmlns+"PropertyGroup") - .Elements(xmlns+"DefineConstants") - .FirstOrDefault(definesConsts=> !string.IsNullOrEmpty(definesConsts.Value)); - - if (DefineConstants != null) - { - DefineConstants.SetValue(DefineConstants.Value + ";" + definesString); - } - } - - private static void ApplyAllowUnsafeBlocks(XElement projectContentElement, XNamespace xmlns) - { - projectContentElement.AddFirst( - new XElement(xmlns + "PropertyGroup", new XElement(xmlns + "AllowUnsafeBlocks", true))); - } - - private static bool IsPlayerProjectFile(string projectFile) - { - return Path.GetFileName(projectFile) == UNITY_PLAYER_PROJECT_NAME; - } - - private static bool IsEditorProjectFile(string projectFile) - { - return Path.GetFileName(projectFile) == UNITY_EDITOR_PROJECT_NAME; - } -#endif - private static void SetXCodeDllReference(string name, XNamespace xmlns, XElement projectContentElement) - { - string unityAppBaseFolder = Path.GetDirectoryName(EditorApplication.applicationPath); - - var xcodeDllPath = Path.Combine(unityAppBaseFolder, Path.Combine("Data/PlaybackEngines/iOSSupport", name)); - if (!File.Exists(xcodeDllPath)) - xcodeDllPath = Path.Combine(unityAppBaseFolder, Path.Combine("PlaybackEngines/iOSSupport", name)); - - if (File.Exists(xcodeDllPath)) - { - var itemGroup = new XElement(xmlns + "ItemGroup"); - var reference = new XElement(xmlns + "Reference"); - reference.Add(new XAttribute("Include", Path.GetFileNameWithoutExtension(xcodeDllPath))); - reference.Add(new XElement(xmlns + "HintPath", xcodeDllPath)); - itemGroup.Add(reference); - projectContentElement.Add(itemGroup); - } - } - - private const string UNITY_REFERENCE_KEYWORD = "-r:"; - /// - /// Handles custom references -r: in "mcs.rsp" - /// - /// - /// - private static void ApplyManualCompilingSettingsReferences(XElement projectContentElement, XNamespace xmlns) - { - if (!File.Exists(PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH)) - return; - - var configFilePath = PROJECT_MANUAL_CONFIG_ABSOLUTE_FILE_PATH; - - if (File.Exists(configFilePath)) - { - var configText = File.ReadAllText(configFilePath); - if (configText.Contains(UNITY_REFERENCE_KEYWORD)) - { - var referenceList = new List(); - var compileFlags = configText.Split(' ', '\n'); - foreach (var flag in compileFlags) - { - var f = flag.Trim(); - if (f.Contains(UNITY_REFERENCE_KEYWORD)) - { - var defineEndPos = f.IndexOf(UNITY_REFERENCE_KEYWORD) + UNITY_REFERENCE_KEYWORD.Length; - var definesSubString = f.Substring(defineEndPos,f.Length - defineEndPos); - definesSubString = definesSubString.Replace(";", ","); - referenceList.AddRange(definesSubString.Split(',')); - } - } - - foreach (var referenceName in referenceList) - { - ApplyCustomReference(referenceName, projectContentElement, xmlns); - } - } - } - } - - private static void ApplyCustomReference(string name, XElement projectContentElement, XNamespace xmlns) - { - var itemGroup = new XElement(xmlns + "ItemGroup"); - var reference = new XElement(xmlns + "Reference"); - reference.Add(new XAttribute("Include", Path.GetFileNameWithoutExtension(name))); - itemGroup.Add(reference); - projectContentElement.Add(itemGroup); - } - - // Set appropriate version - private static void FixTargetFrameworkVersion(XElement projectElement, XNamespace xmlns) - { - var targetFrameworkVersion = projectElement.Elements(xmlns + "PropertyGroup") - .Elements(xmlns + "TargetFrameworkVersion") - .FirstOrDefault(); // Processing csproj files, which are not Unity-generated #56 - if (targetFrameworkVersion != null) - { - int scriptingRuntime = 0; // legacy runtime - try - { - var property = typeof(EditorApplication).GetProperty("scriptingRuntimeVersion"); - scriptingRuntime = (int)property.GetValue(null, null); - if (scriptingRuntime>0) - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "Latest runtime detected."); - } - catch(Exception){} - - if (scriptingRuntime>0) - targetFrameworkVersion.SetValue("v"+RiderPlugin.TargetFrameworkVersion); - else - targetFrameworkVersion.SetValue("v"+RiderPlugin.TargetFrameworkVersionOldMono); - } - } - - private static void SetLangVersion(XElement projectElement, XNamespace xmlns) - { - // Add LangVersion to the .csproj. Unity doesn't generate it (although VSTU does). - // Not strictly necessary, as the Unity plugin for Rider will work it out, but setting - // it makes Rider work if it's not installed. - var langVersion = projectElement.Elements(xmlns + "PropertyGroup").Elements(xmlns + "LangVersion") - .FirstOrDefault(); // Processing csproj files, which are not Unity-generated #56 - if (langVersion != null) - { - langVersion.SetValue(GetLanguageLevel()); - } - else - { - projectElement.AddFirst(new XElement(xmlns + "PropertyGroup", - new XElement(xmlns + "LangVersion", GetLanguageLevel()))); - } - } - - private static string GetLanguageLevel() - { - // https://bitbucket.org/alexzzzz/unity-c-5.0-and-6.0-integration/src - if (Directory.Exists(Path.GetFullPath("CSharp70Support"))) - return "7"; - if (Directory.Exists(Path.GetFullPath("CSharp60Support"))) - return "6"; - - // Unity 5.5 supports C# 6, but only when targeting .NET 4.6. The enum doesn't exist pre Unity 5.5 -#if !UNITY_5_6_OR_NEWER - if ((int)PlayerSettings.apiCompatibilityLevel >= 3) - #else - if ((int) PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup) >= 3) -#endif - return "6"; - - return "4"; - } - - private static Type ourPdb2MdbDriver; - private static Type Pdb2MdbDriver - { - get - { - if (ourPdb2MdbDriver != null) - return ourPdb2MdbDriver; - Assembly assembly; - try - { - var path = Path.GetFullPath(@"Library\resharper-unity-libs\pdb2mdb.exe"); - var bytes = File.ReadAllBytes(path); - assembly = Assembly.Load(bytes); - } - catch (Exception) - { - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "Loading pdb2mdb failed."); - assembly = null; - } - - if (assembly == null) - return null; - var type = assembly.GetType("Pdb2Mdb.Driver"); - if (type == null) - return null; - return ourPdb2MdbDriver = type; - } - } - - public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPath) - { - if (!RiderPlugin.Enabled) - return; - var toBeConverted = importedAssets.Where(a => - a.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && - importedAssets.Any(a1 => a1 == Path.ChangeExtension(a, ".pdb")) && - importedAssets.All(b => b != Path.ChangeExtension(a, ".dll.mdb"))) - .ToArray(); - foreach (var asset in toBeConverted) - { - var pdb = Path.ChangeExtension(asset, ".pdb"); - if (!IsPortablePdb(pdb)) - ConvertSymbolsForAssembly(asset); - else - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, string.Format("mdb generation for Portable pdb is not supported. {0}", pdb)); - } - } - - private static void ConvertSymbolsForAssembly(string asset) - { - if (Pdb2MdbDriver == null) - { - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "FailedToConvertDebugSymbolsNoPdb2mdb."); - return; - } - - var method = Pdb2MdbDriver.GetMethod("Main", BindingFlags.Static | BindingFlags.NonPublic); - if (method == null) - { - RiderPlugin.Log(RiderPlugin.LoggingLevel.Verbose, "WarningFailedToConvertDebugSymbolsPdb2mdbMainIsNull."); - return; - } - - var strArray = new[] { Path.GetFullPath(asset) }; - method.Invoke(null, new object[] { strArray }); - } - - //https://github.com/xamarin/xamarin-android/commit/4e30546f - const uint ppdb_signature = 0x424a5342; - public static bool IsPortablePdb(string filename) - { - try - { - using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) - { - using (var br = new BinaryReader(fs)) - { - return br.ReadUInt32() == ppdb_signature; - } - } - } - catch - { - return false; - } - } - } -} - -namespace Plugins.Editor.JetBrains -{ - [InitializeOnLoad] - public static class RiderPlugin - { - private static bool Initialized; - internal static string SlnFile; - - public static void Log(LoggingLevel level, string initialText) - { - if (level < SelectedLoggingLevel) return; - - var text = "[Rider] "+DateTime.Now.ToString("HH:mm:ss:ff")+" [" + level + "] " + initialText; - - switch (level) - { - case LoggingLevel.Warning: - Debug.LogWarning(text); - break; - default: - Debug.Log(text); - break; - } - } - - private static string GetDefaultApp() - { - var allFoundPaths = GetAllRiderPaths().Select(a=>new FileInfo(a).FullName).ToArray(); - var externalEditor = GetExternalScriptEditor(); - if (!string.IsNullOrEmpty(externalEditor)) - { - var alreadySetPath = new FileInfo(externalEditor).FullName; - if (RiderPathExist(alreadySetPath)) - { - if (!allFoundPaths.Any() || allFoundPaths.Any() && allFoundPaths.Contains(alreadySetPath)) - { - RiderPath = alreadySetPath; - return alreadySetPath; - } - } - } - if (!string.IsNullOrEmpty(RiderPath) && allFoundPaths.Contains(new FileInfo(RiderPath).FullName)) {} - else - RiderPath = allFoundPaths.FirstOrDefault(); - - return RiderPath; - } - - private static string[] GetAllRiderPaths() - { - switch (SystemInfoRiderPlugin.operatingSystemFamily) - { - case OperatingSystemFamily.Windows: - string[] folders = - { - @"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\JetBrains", Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - @"Microsoft\Windows\Start Menu\Programs\JetBrains Toolbox") - }; - - var newPathLnks = folders.Select(b => new DirectoryInfo(b)).Where(a => a.Exists) - .SelectMany(c => c.GetFiles("*Rider*.lnk")).ToArray(); - if (newPathLnks.Any()) - { - var newPaths = newPathLnks - .Select(newPathLnk => new FileInfo(ShortcutResolver.Resolve(newPathLnk.FullName))) - .Where(fi => File.Exists(fi.FullName)) - .ToArray() - .OrderByDescending(fi => FileVersionInfo.GetVersionInfo(fi.FullName).ProductVersion) - .Select(a => a.FullName).ToArray(); - - return newPaths; - } - break; - - case OperatingSystemFamily.MacOSX: - // "/Applications/*Rider*.app" - //"~/Applications/JetBrains Toolbox/*Rider*.app" - string[] foldersMac = - { - "/Applications", Path.Combine(Environment.GetEnvironmentVariable("HOME"), "Applications/JetBrains Toolbox") - }; - var newPathsMac = foldersMac.Select(b => new DirectoryInfo(b)).Where(a => a.Exists) - .SelectMany(c => c.GetDirectories("*Rider*.app")) - .Select(a => a.FullName).ToArray(); - return newPathsMac; - } - return new string[0]; - } - - private static string GetTargetFrameworkVersionDefault(string defaultValue) - { - if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows) - { - var dir = new DirectoryInfo(@"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework"); - if (dir.Exists) - { - var availableVersions = dir.GetDirectories("v*").Select(a => a.Name.Substring(1)) - .Where(v => TryCatch(v, s => { })).ToArray(); - if (availableVersions.Any() && !availableVersions.Contains(defaultValue)) - { - defaultValue = availableVersions.OrderBy(a => new Version(a)).Last(); - } - } - } - return defaultValue; - } - - - public static string TargetFrameworkVersion - { - get - { - return EditorPrefs.GetString("Rider_TargetFrameworkVersion", GetTargetFrameworkVersionDefault("4.6")); - } - set - { - TryCatch(value, val => - { - EditorPrefs.SetString("Rider_TargetFrameworkVersion", val); - }); - } - } - - public static string TargetFrameworkVersionOldMono - { - get - { - return EditorPrefs.GetString("Rider_TargetFrameworkVersionOldMono", GetTargetFrameworkVersionDefault("3.5")); - } - set - { - TryCatch(value, val => - { - EditorPrefs.SetString("Rider_TargetFrameworkVersionOldMono", val); - }); - } - } - - private static bool TryCatch(string value, Action action) - { - try - { - new Version(value); // mono 2.6 doesn't support Version.TryParse - action(value); - return true; - } - catch (ArgumentException) - { - } // can't put loggin here because ot fire on every symbol - catch (FormatException) - { - } - return false; - } - - public static string RiderPath - { - get { return EditorPrefs.GetString("Rider_RiderPath", GetAllRiderPaths().FirstOrDefault()); } - set { EditorPrefs.SetString("Rider_RiderPath", value); } - } - - public enum LoggingLevel - { - Verbose = 0, - Info = 1, - Warning = 2 - } - - public static LoggingLevel SelectedLoggingLevel - { - get { return (LoggingLevel) EditorPrefs.GetInt("Rider_SelectedLoggingLevel", 1); } - set { EditorPrefs.SetInt("Rider_SelectedLoggingLevel", (int) value); } - } - - public static bool RiderInitializedOnce - { - get { return EditorPrefs.GetBool("RiderInitializedOnce", false); } - set { EditorPrefs.SetBool("RiderInitializedOnce", value); } - } - - internal static bool Enabled - { - get - { - var defaultApp = GetExternalScriptEditor(); - return !string.IsNullOrEmpty(defaultApp) && Path.GetFileName(defaultApp).ToLower().Contains("rider"); - } - } - - static RiderPlugin() - { - var riderPath = GetDefaultApp(); - if (!RiderPathExist(riderPath)) - return; - - AddRiderToRecentlyUsedScriptApp(riderPath, "RecentlyUsedScriptApp"); - if (!RiderInitializedOnce) - { - SetExternalScriptEditor(riderPath); - RiderInitializedOnce = true; - } - if (Enabled) - { - InitRiderPlugin(); - } - } - - private static void InitRiderPlugin() - { - var projectDirectory = Directory.GetParent(Application.dataPath).FullName; - - var projectName = Path.GetFileName(projectDirectory); - SlnFile = Path.GetFullPath(string.Format("{0}.sln", projectName)); - - InitializeEditorInstanceJson(); - - RiderAssetPostprocessor.OnGeneratedCSProjectFiles(); - - Log(LoggingLevel.Info, "Rider plugin initialized. You may change the amount of Rider Debug output via Edit -> Preferences -> Rider -> Logging Level"); - Initialized = true; - } - - private static void AddRiderToRecentlyUsedScriptApp(string userAppPath, string recentAppsKey) - { - for (int index = 0; index < 10; ++index) - { - string path = EditorPrefs.GetString(recentAppsKey + (object) index); - if (File.Exists(path) && Path.GetFileName(path).ToLower().Contains("rider")) - return; - } - EditorPrefs.SetString(recentAppsKey + 9, userAppPath); - } - - private static string GetExternalScriptEditor() - { - return EditorPrefs.GetString("kScriptsDefaultApp"); - } - - private static void SetExternalScriptEditor(string path) - { - EditorPrefs.SetString("kScriptsDefaultApp", path); - } - - private static bool RiderPathExist(string path) - { - if (string.IsNullOrEmpty(path)) - return false; - // windows or mac - var fileInfo = new FileInfo(path); - if (!fileInfo.Name.ToLower().Contains("rider")) - return false; - var directoryInfo = new DirectoryInfo(path); - return fileInfo.Exists || (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.MacOSX && - directoryInfo.Exists); - } - - /// - /// Creates and deletes Library/EditorInstance.json containing info about unity instance - /// - private static void InitializeEditorInstanceJson() - { - Log(LoggingLevel.Verbose, "Writing Library/EditorInstance.json"); - - var editorInstanceJsonPath = Path.GetFullPath("Library/EditorInstance.json"); - - File.WriteAllText(editorInstanceJsonPath, string.Format(@"{{ - ""process_id"": {0}, - ""version"": ""{1}"", - ""app_path"": ""{2}"", - ""app_contents_path"": ""{3}"", - ""attach_allowed"": ""{4}"" -}}", Process.GetCurrentProcess().Id, Application.unityVersion, - EditorApplication.applicationPath, - EditorApplication.applicationContentsPath, - EditorPrefs.GetBool("AllowAttachedDebuggingOfEditor", true) - )); - - AppDomain.CurrentDomain.DomainUnload += (sender, args) => - { - Log(LoggingLevel.Verbose, "Deleting Library/EditorInstance.json"); - File.Delete(editorInstanceJsonPath); - }; - } - - /// - /// Asset Open Callback (from Unity) - /// - /// - /// Called when Unity is about to open an asset. - /// - [UnityEditor.Callbacks.OnOpenAssetAttribute()] - static bool OnOpenedAsset(int instanceID, int line) - { - if (Enabled) - { - if (!Initialized) - { - // make sure the plugin was initialized first. - // this can happen in case "Rider" was set as the default scripting app only after this plugin was imported. - InitRiderPlugin(); - } - - // determine asset that has been double clicked in the project view - var selected = EditorUtility.InstanceIDToObject(instanceID); - - var assetFilePath = Path.GetFullPath(AssetDatabase.GetAssetPath(selected)); - if (!(selected.GetType().ToString() == "UnityEditor.MonoScript" || - selected.GetType().ToString() == "UnityEngine.Shader" || - (selected.GetType().ToString() == "UnityEngine.TextAsset" && -#if UNITY_5 || UNITY_5_5_OR_NEWER - EditorSettings.projectGenerationUserExtensions.Contains(Path.GetExtension(assetFilePath).Substring(1)) -#else - EditorSettings.externalVersionControl.Contains(Path.GetExtension(assetFilePath).Substring(1)) -#endif - ))) - return false; - - SyncSolution(); // added to handle opening file, which was just recently created. - if (DetectPortAndOpenFile(line, assetFilePath, SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows)) - return true; - var args = string.Format("{0}{1}{0} --line {2} {0}{3}{0}", "\"", SlnFile, line, assetFilePath); - return CallRider(args); - } - - return false; - } - - - private static bool DetectPortAndOpenFile(int line, string filePath, bool isWindows) - { - if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows) - { - var process = GetRiderProcess(); - if (process == null) - return false; - } - - var ports = Enumerable.Range(63342, 20); - var res = ports.Any(port => - { - var aboutUrl = string.Format("http://localhost:{0}/api/about/", port); - var aboutUri = new Uri(aboutUrl); - - using (var client = new WebClient()) - { - client.Headers.Add("origin", string.Format("http://localhost:{0}", port)); - client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; - - try - { - var responce = CallHttpApi(aboutUri, client); - if (responce.ToLower().Contains("rider")) - { - return HttpOpenFile(line, filePath, isWindows, port, client); - } - } - catch (Exception e) - { - Log(LoggingLevel.Verbose, string.Format("Exception in DetectPortAndOpenFile: {0}", e)); - } - } - return false; - }); - return res; - } - - private static bool HttpOpenFile(int line, string filePath, bool isWindows, int port, WebClient client) - { - var url = string.Format("http://localhost:{0}/api/file?file={1}{2}", port, filePath, - line < 0 - ? "&p=0" - : "&line=" + line); // &p is needed to workaround https://youtrack.jetbrains.com/issue/IDEA-172350 - if (isWindows) - url = string.Format(@"http://localhost:{0}/api/file/{1}{2}", port, filePath, line < 0 ? "" : ":" + line); - - var uri = new Uri(url); - Log(LoggingLevel.Verbose, string.Format("HttpRequestOpenFile({0})", uri.AbsoluteUri)); - - CallHttpApi(uri, client); - ActivateWindow(); - return true; - } - - private static string CallHttpApi(Uri uri, WebClient client) - { - var responseString = client.DownloadString(uri.AbsoluteUri); - Log(LoggingLevel.Verbose, string.Format("CallHttpApi {0} response: {1}", uri.AbsoluteUri, responseString)); - return responseString; - } - - private static bool CallRider(string args) - { - var defaultApp = GetDefaultApp(); - if (!RiderPathExist(defaultApp)) - { - return false; - } - - var proc = new Process(); - if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.MacOSX) - { - proc.StartInfo.FileName = "open"; - proc.StartInfo.Arguments = string.Format("-n {0}{1}{0} --args {2}", "\"", "/" + defaultApp, args); - Log(LoggingLevel.Verbose, string.Format("{0} {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments)); - } - else - { - proc.StartInfo.FileName = defaultApp; - proc.StartInfo.Arguments = args; - Log(LoggingLevel.Verbose, string.Format("{2}{0}{2}" + " {1}", proc.StartInfo.FileName, proc.StartInfo.Arguments, "\"")); - } - - proc.StartInfo.UseShellExecute = false; - proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - proc.StartInfo.CreateNoWindow = true; - proc.StartInfo.RedirectStandardOutput = true; - proc.Start(); - - ActivateWindow(); - return true; - } - - private static void ActivateWindow() - { - if (SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamily.Windows) - { - try - { - var process = GetRiderProcess(); - if (process != null) - { - // Collect top level windows - var topLevelWindows = User32Dll.GetTopLevelWindowHandles(); - // Get process main window title - var windowHandle = topLevelWindows.FirstOrDefault(hwnd => User32Dll.GetWindowProcessId(hwnd) == process.Id); - Log(LoggingLevel.Info, string.Format("ActivateWindow: {0} {1}", process.Id, windowHandle)); - if (windowHandle != IntPtr.Zero) - { - //User32Dll.ShowWindow(windowHandle, 9); //SW_RESTORE = 9 - User32Dll.SetForegroundWindow(windowHandle); - } - } - } - catch (Exception e) - { - Log(LoggingLevel.Warning, "Exception on ActivateWindow: " + e); - } - } - } - - private static Process GetRiderProcess() - { - var process = Process.GetProcesses().FirstOrDefault(p => - { - string processName; - try - { - processName = - p.ProcessName; // some processes like kaspersky antivirus throw exception on attempt to get ProcessName - } - catch (Exception) - { - return false; - } - - return !p.HasExited && processName.ToLower().Contains("rider"); - }); - return process; - } - - // The default "Open C# Project" menu item will use the external script editor to load the .sln - // file, but unless Unity knows the external script editor can properly load solutions, it will - // also launch MonoDevelop (or the OS registered app for .sln files). This menu item side steps - // that issue, and opens the solution in Rider without opening MonoDevelop as well. - // Unity 2017.1 and later recognise Rider as an app that can load solutions, so this menu isn't - // needed in newer versions. - [MenuItem("Assets/Open C# Project in Rider", false, 1000)] - static void MenuOpenProject() - { - // Force the project files to be sync - SyncSolution(); - - // Load Project - CallRider(string.Format("{0}{1}{0}", "\"", SlnFile)); - } - - [MenuItem("Assets/Open C# Project in Rider", true, 1000)] - static bool ValidateMenuOpenProject() - { - return Enabled; - } - - /// - /// Force Unity To Write Project File - /// - private static void SyncSolution() - { - System.Type T = System.Type.GetType("UnityEditor.SyncVS,UnityEditor"); - System.Reflection.MethodInfo SyncSolution = T.GetMethod("SyncSolution", - System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); - SyncSolution.Invoke(null, null); - } - - /// - /// JetBrains Rider Integration Preferences Item - /// - /// - /// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off - /// - [PreferenceItem("Rider")] - static void RiderPreferencesItem() - { - EditorGUILayout.BeginVertical(); - EditorGUI.BeginChangeCheck(); - - var alternatives = GetAllRiderPaths(); - if (alternatives.Any()) - { - int index = Array.IndexOf(alternatives, RiderPath); - var alts = alternatives.Select(s => s.Replace("/", ":")) - .ToArray(); // hack around https://fogbugz.unity3d.com/default.asp?940857_tirhinhe3144t4vn - RiderPath = alternatives[EditorGUILayout.Popup("Rider executable:", index == -1 ? 0 : index, alts)]; - if (EditorGUILayout.Toggle(new GUIContent("Rider is default editor"), Enabled)) - { - SetExternalScriptEditor(RiderPath); - EditorGUILayout.HelpBox("Unckecking will restore default external editor.", MessageType.None); - } - else - { - SetExternalScriptEditor(string.Empty); - EditorGUILayout.HelpBox("Checking will set Rider as default external editor", MessageType.None); - } - } - - GUILayout.BeginVertical(); - string status = "TargetFrameworkVersion for Runtime"; - EditorGUILayout.TextArea(status, EditorStyles.boldLabel); - var help = @"TargetFramework >= 4.5 is recommended."; - TargetFrameworkVersion = - EditorGUILayout.TextField( - new GUIContent("NET 4.6", - help), TargetFrameworkVersion); - EditorGUILayout.HelpBox(help, MessageType.None); - var helpOldMono = @"TargetFramework = 3.5 is recommended. - - With 4.5 Rider may show ambiguous references in UniRx."; - - TargetFrameworkVersionOldMono = - EditorGUILayout.TextField( - new GUIContent("NET 3.5", - helpOldMono), TargetFrameworkVersionOldMono); - EditorGUILayout.HelpBox(helpOldMono, MessageType.None); - - GUILayout.EndVertical(); - - EditorGUI.EndChangeCheck(); - - EditorGUI.BeginChangeCheck(); - - var loggingMsg = - @"Sets the amount of Rider Debug output. If you are about to report an issue, please select Verbose logging level and attach Unity console output to the issue."; - SelectedLoggingLevel = (LoggingLevel) EditorGUILayout.EnumPopup(new GUIContent("Logging Level", loggingMsg), SelectedLoggingLevel); - EditorGUILayout.HelpBox(loggingMsg, MessageType.None); - - EditorGUI.EndChangeCheck(); - - var url = "https://github.com/JetBrains/resharper-unity"; - LinkButton(url, url); - -/* if (GUILayout.Button("reset RiderInitializedOnce = false")) - { - RiderInitializedOnce = false; - }*/ - - EditorGUILayout.EndVertical(); - } - - private static void LinkButton(string caption, string url) - { - var style = GUI.skin.label; - style.richText = true; - caption = string.Format("{0}", caption); - - bool bClicked = GUILayout.Button(caption, style); - - var rect = GUILayoutUtility.GetLastRect(); - rect.width = style.CalcSize(new GUIContent(caption)).x; - EditorGUIUtility.AddCursorRect(rect, MouseCursor.Link); - - if (bClicked) - Application.OpenURL(url); - } - - #region SystemInfoRiderPlugin - - private static class SystemInfoRiderPlugin - { - public static OperatingSystemFamily operatingSystemFamily - { - get - { -#if UNITY_5_5_OR_NEWER -return SystemInfo.operatingSystemFamily; -#else - if (SystemInfo.operatingSystem.StartsWith("Mac", StringComparison.InvariantCultureIgnoreCase)) - { - return OperatingSystemFamily.MacOSX; - } - if (SystemInfo.operatingSystem.StartsWith("Win", StringComparison.InvariantCultureIgnoreCase)) - { - return OperatingSystemFamily.Windows; - } - if (SystemInfo.operatingSystem.StartsWith("Lin", StringComparison.InvariantCultureIgnoreCase)) - { - return OperatingSystemFamily.Linux; - } - return OperatingSystemFamily.Other; -#endif - } - } - } -#if !UNITY_5_5_OR_NEWER - enum OperatingSystemFamily - { - Other, - MacOSX, - Windows, - Linux, - } -#endif - #endregion - - static class User32Dll - { - - /// - /// Gets the ID of the process that owns the window. - /// Note that creating a wrapper for that is very expensive because it causes an enumeration of all the system processes to happen. - /// - public static int GetWindowProcessId(IntPtr hwnd) - { - uint dwProcessId; - GetWindowThreadProcessId(hwnd, out dwProcessId); - return unchecked((int) dwProcessId); - } - - /// - /// Lists the handles of all the top-level windows currently available in the system. - /// - public static List GetTopLevelWindowHandles() - { - var retval = new List(); - EnumWindowsProc callback = (hwnd, param) => - { - retval.Add(hwnd); - return 1; - }; - EnumWindows(Marshal.GetFunctionPointerForDelegate(callback), IntPtr.Zero); - GC.KeepAlive(callback); - return retval; - } - - public delegate Int32 EnumWindowsProc(IntPtr hwnd, IntPtr lParam); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, - ExactSpelling = true)] - public static extern Int32 EnumWindows(IntPtr lpEnumFunc, IntPtr lParam); - - [DllImport("user32.dll", SetLastError = true)] - static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, - ExactSpelling = true)] - public static extern Int32 SetForegroundWindow(IntPtr hWnd); - - [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, - ExactSpelling = true)] - public static extern UInt32 ShowWindow(IntPtr hWnd, Int32 nCmdShow); - } - - static class ShortcutResolver - { - #region Signitures imported from http://pinvoke.net - - [DllImport("shfolder.dll", CharSet = CharSet.Auto)] - internal static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath); - - [Flags()] - enum SLGP_FLAGS - { - /// Retrieves the standard short (8.3 format) file name - SLGP_SHORTPATH = 0x1, - - /// Retrieves the Universal Naming Convention (UNC) path name of the file - SLGP_UNCPRIORITY = 0x2, - - /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded - SLGP_RAWPATH = 0x4 - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct WIN32_FIND_DATAW - { - public uint dwFileAttributes; - public long ftCreationTime; - public long ftLastAccessTime; - public long ftLastWriteTime; - public uint nFileSizeHigh; - public uint nFileSizeLow; - public uint dwReserved0; - public uint dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; - } - - [Flags()] - enum SLR_FLAGS - { - /// - /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, - /// the high-order word of fFlags can be set to a time-out value that specifies the - /// maximum amount of time to be spent resolving the link. The function returns if the - /// link cannot be resolved within the time-out duration. If the high-order word is set - /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds - /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out - /// duration, in milliseconds. - /// - SLR_NO_UI = 0x1, - - /// Obsolete and no longer used - SLR_ANY_MATCH = 0x2, - - /// If the link object has changed, update its path and list of identifiers. - /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine - /// whether or not the link object has changed. - SLR_UPDATE = 0x4, - - /// Do not update the link information - SLR_NOUPDATE = 0x8, - - /// Do not execute the search heuristics - SLR_NOSEARCH = 0x10, - - /// Do not use distributed link tracking - SLR_NOTRACK = 0x20, - - /// Disable distributed link tracking. By default, distributed link tracking tracks - /// removable media across multiple devices based on the volume name. It also uses the - /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter - /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. - SLR_NOLINKINFO = 0x40, - - /// Call the Microsoft Windows Installer - SLR_INVOKE_MSI = 0x80 - } - - - /// The IShellLink interface allows Shell links to be created, modified, and resolved - [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] - interface IShellLinkW - { - /// Retrieves the path and file name of a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); - - /// Retrieves the list of item identifiers for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetIDList(out IntPtr ppidl); - - /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetIDList(IntPtr pidl); - - /// Retrieves the description string for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); - - /// Sets the description for a Shell link object. The description can be any application-defined string - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); - - /// Retrieves the name of the working directory for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); - - /// Sets the name of the working directory for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); - - /// Retrieves the command-line arguments associated with a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); - - /// Sets the command-line arguments for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); - - /// Retrieves the hot key for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetHotkey(out short pwHotkey); - - /// Sets a hot key for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetHotkey(short wHotkey); - - /// Retrieves the show command for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetShowCmd(out int piShowCmd); - - /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetShowCmd(int iShowCmd); - - /// Retrieves the location (path and index) of the icon for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); - - /// Sets the location (path and index) of the icon for a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); - - /// Sets the relative path to the Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); - - /// Attempts to find the target of a Shell link, even if it has been moved or renamed - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); - - /// Sets the path and file name of a Shell link object - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); - } - - [ComImport, Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IPersist - { - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetClassID(out Guid pClassID); - } - - - [ComImport, Guid("0000010b-0000-0000-C000-000000000046"), - InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IPersistFile : IPersist - { - [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - new void GetClassID(out Guid pClassID); - - [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - int IsDirty(); - - [MethodImpl(MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void Load([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode); - - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [In, MarshalAs(UnmanagedType.Bool)] bool fRemember); - - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName); - - [MethodImpl (MethodImplOptions.InternalCall | MethodImplOptions.PreserveSig, MethodCodeType = MethodCodeType.Runtime)] - void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName); - } - - const uint STGM_READ = 0; - const int MAX_PATH = 260; - - // CLSID_ShellLink from ShlGuid.h - [ - ComImport(), - Guid("00021401-0000-0000-C000-000000000046") - ] - public class ShellLink - { - } - - #endregion - - public static string Resolve(string filename) - { - ShellLink link = new ShellLink(); - ((IPersistFile) link).Load(filename, STGM_READ); - // If I can get hold of the hwnd call resolve first. This handles moved and renamed files. - // ((IShellLinkW)link).Resolve(hwnd, 0) - StringBuilder sb = new StringBuilder(MAX_PATH); - WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - ((IShellLinkW) link).GetPath(sb, sb.Capacity, out data, 0); - return sb.ToString(); - } - } - } -} - -// Developed using JetBrains Rider =) diff --git a/Assets/Plugins/Editor/JetBrains/Unity3DRider.cs.meta b/Assets/Plugins/Editor/JetBrains/Unity3DRider.cs.meta deleted file mode 100644 index dd99a7c5..00000000 --- a/Assets/Plugins/Editor/JetBrains/Unity3DRider.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 935b331e675d34b50ada3969f593ca5e -timeCreated: 1497891644 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options.meta b/Assets/Plugins/SharpCompress.meta similarity index 67% rename from Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options.meta rename to Assets/Plugins/SharpCompress.meta index 46fcea3c..c4fe6ca9 100644 --- a/Assets/Editor/AssetStoreBatchMode/lib/NDesk.Options.meta +++ b/Assets/Plugins/SharpCompress.meta @@ -1,7 +1,7 @@ fileFormatVersion: 2 -guid: 9704e6f4a1a8e4486a36a7d2f1ae1bef +guid: 9442192748fe1458385848f398ce4813 folderAsset: yes -timeCreated: 1490992909 +timeCreated: 1531727640 licenseType: Free DefaultImporter: userData: diff --git a/Assets/Plugins/SharpCompress/SharpCompress.dll b/Assets/Plugins/SharpCompress/SharpCompress.dll new file mode 100644 index 00000000..874b0b13 Binary files /dev/null and b/Assets/Plugins/SharpCompress/SharpCompress.dll differ diff --git a/Assets/Plugins/SharpCompress/SharpCompress.dll.meta b/Assets/Plugins/SharpCompress/SharpCompress.dll.meta new file mode 100644 index 00000000..b0d4b92b --- /dev/null +++ b/Assets/Plugins/SharpCompress/SharpCompress.dll.meta @@ -0,0 +1,24 @@ +fileFormatVersion: 2 +guid: 5593d402ca03c412da40ffe6349d5dfe +timeCreated: 1531727644 +licenseType: Free +PluginImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + platformData: + Any: + enabled: 1 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets.meta b/Assets/StreamingAssets.meta deleted file mode 100644 index 42de2fcc..00000000 --- a/Assets/StreamingAssets.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: bcb6c9484f38788498fa80a031b250bc -folderAsset: yes -timeCreated: 1478623399 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/.gitignore b/Assets/StreamingAssets/.gitignore deleted file mode 100644 index 23eaeac5..00000000 --- a/Assets/StreamingAssets/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/patcher.versioninfo -/patcher.versioninfo.meta \ No newline at end of file diff --git a/Assets/StreamingAssets/torrent-client.meta b/Assets/StreamingAssets/torrent-client.meta deleted file mode 100644 index 289ccbd3..00000000 --- a/Assets/StreamingAssets/torrent-client.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 2e1b87abc3f51ce47a294ae4be31c618 -folderAsset: yes -timeCreated: 1478623410 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/linux64.meta b/Assets/StreamingAssets/torrent-client/linux64.meta deleted file mode 100644 index 2b578323..00000000 --- a/Assets/StreamingAssets/torrent-client/linux64.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: b8ef115066f2343008b4bf98b0c93725 -folderAsset: yes -timeCreated: 1479229537 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/linux64/libboost_random.so.1.62.0.meta b/Assets/StreamingAssets/torrent-client/linux64/libboost_random.so.1.62.0.meta deleted file mode 100644 index 3cbca8f0..00000000 --- a/Assets/StreamingAssets/torrent-client/linux64/libboost_random.so.1.62.0.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 795119c990cc946dba39f44d414e4edd -timeCreated: 1479229539 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/linux64/libboost_system.so.1.62.0.meta b/Assets/StreamingAssets/torrent-client/linux64/libboost_system.so.1.62.0.meta deleted file mode 100644 index 42ee8862..00000000 --- a/Assets/StreamingAssets/torrent-client/linux64/libboost_system.so.1.62.0.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: f61b6bb729b5b48749453e7b42a0b570 -timeCreated: 1479229540 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/linux64/libtorrent-rasterbar.so.8.meta b/Assets/StreamingAssets/torrent-client/linux64/libtorrent-rasterbar.so.8.meta deleted file mode 100644 index 4491fbe8..00000000 --- a/Assets/StreamingAssets/torrent-client/linux64/libtorrent-rasterbar.so.8.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bea92716ef1c44ac6a5ca8c8fba192b5 -timeCreated: 1479229540 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/linux64/torrent-client b/Assets/StreamingAssets/torrent-client/linux64/torrent-client deleted file mode 100755 index 2c8b2676..00000000 Binary files a/Assets/StreamingAssets/torrent-client/linux64/torrent-client and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/linux64/torrent-client.meta b/Assets/StreamingAssets/torrent-client/linux64/torrent-client.meta deleted file mode 100644 index 3405d944..00000000 --- a/Assets/StreamingAssets/torrent-client/linux64/torrent-client.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 3d3e96954d762449282da6b166762132 -timeCreated: 1479229538 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/osx64.meta b/Assets/StreamingAssets/torrent-client/osx64.meta deleted file mode 100644 index 1845d388..00000000 --- a/Assets/StreamingAssets/torrent-client/osx64.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: d5a6e7ad8c10049f98f7e9f57ce2396a -folderAsset: yes -timeCreated: 1479156235 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/osx64/libboost_chrono.dylib b/Assets/StreamingAssets/torrent-client/osx64/libboost_chrono.dylib deleted file mode 100755 index 0629cebc..00000000 Binary files a/Assets/StreamingAssets/torrent-client/osx64/libboost_chrono.dylib and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/osx64/libboost_chrono.dylib.meta b/Assets/StreamingAssets/torrent-client/osx64/libboost_chrono.dylib.meta deleted file mode 100644 index 308337dd..00000000 --- a/Assets/StreamingAssets/torrent-client/osx64/libboost_chrono.dylib.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c7bb45290f2144f059ff57c3d455031f -timeCreated: 1479156236 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/osx64/libboost_random.dylib b/Assets/StreamingAssets/torrent-client/osx64/libboost_random.dylib deleted file mode 100755 index abcb7395..00000000 Binary files a/Assets/StreamingAssets/torrent-client/osx64/libboost_random.dylib and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/osx64/libboost_random.dylib.meta b/Assets/StreamingAssets/torrent-client/osx64/libboost_random.dylib.meta deleted file mode 100644 index f09b23cd..00000000 --- a/Assets/StreamingAssets/torrent-client/osx64/libboost_random.dylib.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 15cfca6350b0f4a8ebbda74ce6480784 -timeCreated: 1479156236 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/osx64/libboost_system.dylib b/Assets/StreamingAssets/torrent-client/osx64/libboost_system.dylib deleted file mode 100755 index ae7c5497..00000000 Binary files a/Assets/StreamingAssets/torrent-client/osx64/libboost_system.dylib and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/osx64/libboost_system.dylib.meta b/Assets/StreamingAssets/torrent-client/osx64/libboost_system.dylib.meta deleted file mode 100644 index a78d0182..00000000 --- a/Assets/StreamingAssets/torrent-client/osx64/libboost_system.dylib.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 1395a9372a0924e69ada3344a3a770f9 -timeCreated: 1479156236 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/osx64/libtorrent-rasterbar.8.dylib b/Assets/StreamingAssets/torrent-client/osx64/libtorrent-rasterbar.8.dylib deleted file mode 100755 index fdba0136..00000000 Binary files a/Assets/StreamingAssets/torrent-client/osx64/libtorrent-rasterbar.8.dylib and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/osx64/libtorrent-rasterbar.8.dylib.meta b/Assets/StreamingAssets/torrent-client/osx64/libtorrent-rasterbar.8.dylib.meta deleted file mode 100644 index dda21a86..00000000 --- a/Assets/StreamingAssets/torrent-client/osx64/libtorrent-rasterbar.8.dylib.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: f51c07315433c4d219e63165c7b73eee -timeCreated: 1479156236 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/osx64/torrent-client b/Assets/StreamingAssets/torrent-client/osx64/torrent-client deleted file mode 100755 index c6a56e30..00000000 Binary files a/Assets/StreamingAssets/torrent-client/osx64/torrent-client and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/osx64/torrent-client.meta b/Assets/StreamingAssets/torrent-client/osx64/torrent-client.meta deleted file mode 100644 index d6fd6b8e..00000000 --- a/Assets/StreamingAssets/torrent-client/osx64/torrent-client.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 088656fbc8b2940faa0ddbd6f8b216d7 -timeCreated: 1479156235 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/win.meta b/Assets/StreamingAssets/torrent-client/win.meta deleted file mode 100644 index 98415d4f..00000000 --- a/Assets/StreamingAssets/torrent-client/win.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 3cdea0f236676b248b2ac4bfcbbb5257 -folderAsset: yes -timeCreated: 1478623420 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/StreamingAssets/torrent-client/win/torrent-client.exe b/Assets/StreamingAssets/torrent-client/win/torrent-client.exe deleted file mode 100644 index 745755b8..00000000 Binary files a/Assets/StreamingAssets/torrent-client/win/torrent-client.exe and /dev/null differ diff --git a/Assets/StreamingAssets/torrent-client/win/torrent-client.exe.meta b/Assets/StreamingAssets/torrent-client/win/torrent-client.exe.meta deleted file mode 100644 index 60f5dc76..00000000 --- a/Assets/StreamingAssets/torrent-client/win/torrent-client.exe.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4ffc693ac6763f44086fd3b01c40631a -timeCreated: 1478623461 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/UnityTestTools/IntegrationTestsFramework/TestRunner/TestRunnerConfigurator.cs b/Assets/UnityTestTools/IntegrationTestsFramework/TestRunner/TestRunnerConfigurator.cs index 8e7f322a..b0b270d4 100644 --- a/Assets/UnityTestTools/IntegrationTestsFramework/TestRunner/TestRunnerConfigurator.cs +++ b/Assets/UnityTestTools/IntegrationTestsFramework/TestRunner/TestRunnerConfigurator.cs @@ -59,7 +59,7 @@ public UnityEditor.EditorBuildSettingsScene[] GetPreviousScenesToRestore() { return (UnityEditor.EditorBuildSettingsScene[] )serializer.Deserialize(textReader); } - catch (System.Xml.XmlException e) + catch (System.Xml.XmlException) { return null; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c03ca44..ab9d6d17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + +## [3.11.0] +### Added +- Support for LZMA2 compression using XZ +- Sending all events to Statistics Reporting Service +- Support for main_executable and main_executable_args fields in AppVersion +- Added processing of --online or --offline command line argument +- Sending 'patcher_started' event to Statistics Reporting Service +- Custom building options under `Tools/Build` +- Handling failures when accessing the file system +- Attaching "system-info" to Sentry events as tag +- Support for PK_OFFICIAL define +- Displaying "Stalled..." instead of "Downloading package..." if the download speed reaches 0 B/s +- capabilities field in the manifest + +### Changed +- Linux launch script +- Download speed will now be displayed based on the average of download speeds in the last 2 seconds +- Patcher will no longer clear the progress bar after updating + +### Fixed +- Invalid display of progress value when unarchiving +- Wrapping the GZipStream input to avoid errors +- Fixed all warnings that appear when launching on 5.3.4f1 +- Freeze or crash after closing the patcher +- Window size issue on Linux +- Stalling issue due to high request timeout and delays + +### Removed +- Torrent downloading +- StandaloneOSXUniversal architecture from building options + ## [3.10.3] ### Fixed - Fix issue with locating screensize file