diff --git a/Packages/com.unity.asset-store-tools/CHANGELOG.md b/Packages/com.unity.asset-store-tools/CHANGELOG.md index b40e58a..d51cbe8 100644 --- a/Packages/com.unity.asset-store-tools/CHANGELOG.md +++ b/Packages/com.unity.asset-store-tools/CHANGELOG.md @@ -1,6 +1,37 @@ # Changelog All notable changes to this package will be documented in this file. +## [12.0.1] - 2025-01-16 + +### Preview Generator changes +- Updated generated preview collection UI to display the asset extension +- Fixed an issue with some prefab and model asset types not generating previews +- Fixed an error that could occur when changing scenes after deleting a preview source asset + +## [12.0.0] - 2025-01-13 + +### General changes +- The code comprising the Asset Store Publishing Tools has been refactored. +- Added dependency on Newtonsoft Json + +### Uploader changes +- Updated window to retain its state if closed unless a domain reload occurs +- Added option to generate higher resolution asset previews when exporting +- Fixed a rare issue where authentication would fail +- Minor UI tweaks + +### Validator changes +- Added validation tests for: + - Package naming + - Project Template assets +- Updated the Type Namespace validation test to check for Unity top level namespaces + +### Exporter changes +- Updated how asset previews are generated/included for the package that is being exported + +### Preview Generator +- Added a Preview Generator window that can be used to pre-generate and inspect package previews before exporting + ## [11.4.4] - 2024-11-29 ### Validator Changes diff --git a/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta b/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta index 04ab54f..9ca86c3 100644 --- a/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta +++ b/Packages/com.unity.asset-store-tools/CHANGELOG.md.meta @@ -9,6 +9,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 + packageVersion: 12.0.1 assetPath: Packages/com.unity.asset-store-tools/CHANGELOG.md - uploadId: 712972 + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements.meta b/Packages/com.unity.asset-store-tools/Editor/Api.meta similarity index 77% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements.meta rename to Packages/com.unity.asset-store-tools/Editor/Api.meta index 87a66a2..0cf8e3f 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Api.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3eb6991a3db8cc34dad63504bc6c3c0e +guid: d48a2325d352e7a4cae56d3f8eeaab2d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions.meta similarity index 77% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login.meta rename to Packages/com.unity.asset-store-tools/Editor/Api/Abstractions.meta index 0a7ccff..80ccfb1 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d6e2d6bcfe000764e9330d78017e32bc +guid: 25799fb31cd475347af7f5442c231797 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs new file mode 100644 index 0000000..84d1fa8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs @@ -0,0 +1,48 @@ +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +namespace AssetStoreTools.Api +{ + internal abstract class AuthenticationBase : IAuthenticationType + { + protected Uri LoginUrl = ApiUtility.CreateUri(Constants.Api.AuthenticateUrl, true); + protected FormUrlEncodedContent AuthenticationContent; + + protected FormUrlEncodedContent GetAuthenticationContent(params KeyValuePair[] content) + { + var baseContent = Constants.Api.DefaultAssetStoreQuery(); + + try { baseContent.Add("license_hash", ApiUtility.GetLicenseHash()); } catch { ASDebug.LogWarning("Could not retrieve license hash"); } + try { baseContent.Add("hardware_hash", ApiUtility.GetHardwareHash()); } catch { ASDebug.LogWarning("Could not retrieve hardware hash"); } + + foreach (var extraContent in content) + { + baseContent.Add(extraContent.Key, extraContent.Value); + } + + return new FormUrlEncodedContent(baseContent); + } + + protected AuthenticationResponse ParseResponse(HttpResponseMessage response) + { + try + { + response.EnsureSuccessStatusCode(); + var responseString = response.Content.ReadAsStringAsync().Result; + return new AuthenticationResponse(responseString); + } + catch (HttpRequestException e) + { + return new AuthenticationResponse(response.StatusCode, e) { Success = false }; + } + } + + public abstract Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs.meta new file mode 100644 index 0000000..2607489 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f677e03f1be1048439a1fa5e7a0a37b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/AuthenticationBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs new file mode 100644 index 0000000..86db5cd --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs @@ -0,0 +1,21 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IAssetStoreApi + { + Task GetLatestAssetStoreToolsVersion(CancellationToken cancellationToken = default); + Task Authenticate(IAuthenticationType authenticationType, CancellationToken cancellationToken = default); + void Deauthenticate(); + Task GetPackages(CancellationToken cancellationToken = default); + Task GetCategories(CancellationToken cancellationToken = default); + Task GetPackageThumbnail(Package package, CancellationToken cancellationToken = default); + Task RefreshPackageMetadata(Package package, CancellationToken cancellationToken = default); + Task GetPackageUploadedVersions(Package package, CancellationToken cancellationToken = default); + Task UploadPackage(IPackageUploader uploader, IProgress progress = null, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs.meta new file mode 100644 index 0000000..6caa9aa --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e616488c25d278741bb0d08168219309 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreApi.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs new file mode 100644 index 0000000..f6a6d3e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs @@ -0,0 +1,18 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IAssetStoreClient + { + void SetSessionId(string sessionId); + void ClearSessionId(); + + Task Get(Uri uri, CancellationToken cancellationToken = default); + Task Post(Uri uri, HttpContent content, CancellationToken cancellationToken = default); + Task Put(Uri uri, HttpContent content, CancellationToken cancellationToken = default); + Task Send(HttpRequestMessage request, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs.meta new file mode 100644 index 0000000..eeb360c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b2bbadec62178cc4189e605367b219e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAssetStoreClient.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs new file mode 100644 index 0000000..8fa5c78 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Api.Responses; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IAuthenticationType + { + Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs.meta new file mode 100644 index 0000000..a713c84 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0000dcd6975bc8e4abc546a19f194040 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IAuthenticationType.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs new file mode 100644 index 0000000..ef7c89f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Api.Responses; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal interface IPackageUploader + { + Task Upload(IAssetStoreClient client, IProgress progress, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs.meta new file mode 100644 index 0000000..b8c5fa0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0fc6c47b1c0a65540a40efbf1491193b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/IPackageUploader.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs new file mode 100644 index 0000000..e26bfc1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Api.Responses; +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal abstract class PackageUploaderBase : IPackageUploader + { + protected const int UploadChunkSizeBytes = 32768; + protected const int UploadResponseTimeoutMs = 10000; + + protected abstract void ValidateSettings(); + public abstract Task Upload(IAssetStoreClient client, IProgress progress = null, CancellationToken cancellationToken = default); + + protected void EnsureSuccessResponse(HttpResponseMessage response) + { + try + { + response.EnsureSuccessStatusCode(); + } + catch + { + throw new Exception(response.Content.ReadAsStringAsync().Result); + } + } + + protected void WaitForUploadCompletion(Task response, FileStream requestFileStream, IProgress progress, CancellationToken cancellationToken) + { + // Progress tracking + int updateIntervalMs = 100; + bool allBytesSent = false; + DateTime timeOfCompletion = default; + + while (!response.IsCompleted) + { + float uploadProgress = (float)requestFileStream.Position / requestFileStream.Length * 100; + progress?.Report(uploadProgress); + Thread.Sleep(updateIntervalMs); + + // A timeout for rare cases, when package uploading reaches 100%, but Put task IsComplete value remains 'False' + if (requestFileStream.Position == requestFileStream.Length) + { + if (!allBytesSent) + { + allBytesSent = true; + timeOfCompletion = DateTime.UtcNow; + } + else if (DateTime.UtcNow.Subtract(timeOfCompletion).TotalMilliseconds > UploadResponseTimeoutMs) + { + throw new TimeoutException(); + } + } + } + } + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs.meta new file mode 100644 index 0000000..3b255ee --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2718ddd16e425ba4a82ab973724bcff7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Abstractions/PackageUploaderBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs b/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs new file mode 100644 index 0000000..545d6d4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs @@ -0,0 +1,76 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditorInternal; + +namespace AssetStoreTools.Api +{ + internal class ApiUtility + { + public static Uri CreateUri(string url, bool includeDefaultAssetStoreQuery) => CreateUri(url, null, includeDefaultAssetStoreQuery); + public static Uri CreateUri(string url, IDictionary queryParameters, bool includeDefaultAssetStoreQuery) + { + IDictionary fullQueryParameters = includeDefaultAssetStoreQuery ? + Constants.Api.DefaultAssetStoreQuery() : new Dictionary(); + + if (queryParameters != null && queryParameters.Count > 0) + { + foreach (var kvp in queryParameters) + fullQueryParameters.Add(kvp); + } + + var builder = new UriBuilder(url); + if (fullQueryParameters.Count == 0) + return builder.Uri; + + var fullQueryParameterString = string.Empty; + foreach (var queryParam in fullQueryParameters) + { + var escapedValue = queryParam.Value != null ? Uri.EscapeDataString(queryParam.Value) : string.Empty; + fullQueryParameterString += $"{queryParam.Key}={escapedValue}&"; + } + fullQueryParameterString = fullQueryParameterString.Remove(fullQueryParameterString.Length - 1); + + builder.Query = fullQueryParameterString; + return builder.Uri; + } + + public static List CombinePackageData(List mainPackageData, List extraPackageData, List categoryData) + { + foreach (var package in mainPackageData) + { + var extraData = extraPackageData.FirstOrDefault(x => package.PackageId == x.PackageId); + + if (extraData == null) + { + ASDebug.LogWarning($"Could not find extra data for Package {package.PackageId}"); + continue; + } + + var categoryId = extraData.CategoryId; + var category = categoryData.FirstOrDefault(x => x.Id.ToString() == categoryId); + if (category != null) + package.Category = category.Name; + else + package.Category = "Unknown"; + + package.Modified = extraData.Modified; + package.Size = extraData.Size; + } + + return mainPackageData; + } + + public static string GetLicenseHash() + { + return InternalEditorUtility.GetAuthToken().Substring(0, 40); + } + + public static string GetHardwareHash() + { + return InternalEditorUtility.GetAuthToken().Substring(40, 40); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs.meta new file mode 100644 index 0000000..0259bfc --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5becec0b3c0ba274fb0b01544e63b6c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/ApiUtility.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs new file mode 100644 index 0000000..a8530dc --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs @@ -0,0 +1,268 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class AssetStoreApi : IAssetStoreApi + { + private IAssetStoreClient _client; + + public AssetStoreApi(IAssetStoreClient client) + { + _client = client; + } + + public async Task GetLatestAssetStoreToolsVersion(CancellationToken cancellationToken = default) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.AssetStoreToolsLatestVersionUrl, false); + var response = await _client.Get(uri, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + var responseStr = response.Content.ReadAsStringAsync().Result; + return new AssetStoreToolsVersionResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new AssetStoreToolsVersionResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new AssetStoreToolsVersionResponse() { Success = false, Exception = e }; + } + } + + public async Task Authenticate(IAuthenticationType authenticationType, CancellationToken cancellationToken = default) + { + try + { + var loginResponse = await authenticationType.Authenticate(_client, cancellationToken); + if (loginResponse.Success) + { + _client.SetSessionId(loginResponse.User.SessionId); + } + + return loginResponse; + } + catch (OperationCanceledException e) + { + return new AuthenticationResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new AuthenticationResponse() { Success = false, Exception = e }; + } + } + + public void Deauthenticate() + { + _client.ClearSessionId(); + } + + public async Task GetPackages(CancellationToken cancellationToken = default) + { + try + { + var mainDataResponse = await GetPackageDataMain(cancellationToken); + if (!mainDataResponse.Success) + throw mainDataResponse.Exception; + var additionalDataResponse = await GetPackageDataExtra(cancellationToken); + if (!additionalDataResponse.Success) + throw additionalDataResponse.Exception; + var categoryDataResponse = await GetCategories(cancellationToken); + if (!categoryDataResponse.Success) + throw categoryDataResponse.Exception; + + var joinedData = ApiUtility.CombinePackageData(mainDataResponse.Packages, additionalDataResponse.Packages, categoryDataResponse.Categories); + return new PackagesDataResponse() { Success = true, Packages = joinedData }; + } + catch (OperationCanceledException e) + { + return new PackagesDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackagesDataResponse() { Success = false, Exception = e }; + } + } + + private async Task GetPackageDataMain(CancellationToken cancellationToken) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetPackagesUrl, true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new PackagesDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new PackagesDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackagesDataResponse() { Success = false, Exception = e }; + } + } + + private async Task GetPackageDataExtra(CancellationToken cancellationToken) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetPackagesAdditionalDataUrl, true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new PackagesAdditionalDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new PackagesAdditionalDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackagesAdditionalDataResponse() { Success = false, Exception = e }; + } + } + + public async Task GetCategories(CancellationToken cancellationToken) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetCategoriesUrl, true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new CategoryDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new CategoryDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new CategoryDataResponse() { Success = false, Exception = e }; + } + } + + public async Task GetPackageThumbnail(Package package, CancellationToken cancellationToken = default) + { + try + { + if (string.IsNullOrEmpty(package.IconUrl)) + throw new Exception($"Could not retrieve thumbnail for package {package.PackageId} - icon url is null"); + + var response = await _client.Get(new Uri(package.IconUrl), cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseBytes = response.Content.ReadAsByteArrayAsync().Result; + return new PackageThumbnailResponse(responseBytes); + } + catch (OperationCanceledException e) + { + return new PackageThumbnailResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackageThumbnailResponse() { Success = false, Exception = e }; + } + } + + public async Task RefreshPackageMetadata(Package package, CancellationToken cancellationToken = default) + { + try + { + var refreshedPackage = JObject.FromObject(package).DeepClone().ToObject(); + + var packagesResponse = await GetPackageDataExtra(cancellationToken); + if (!packagesResponse.Success) + throw packagesResponse.Exception; + + // Find the updated package data in the latest data json + var packageRefreshSource = packagesResponse.Packages.FirstOrDefault(x => x.PackageId == refreshedPackage.PackageId); + if (packageRefreshSource == null) + return new RefreshedPackageDataResponse() { Success = false, Exception = new MissingMemberException($"Unable to find downloaded package data for package id {package.PackageId}") }; + + // Retrieve the category map + var categoryData = await GetCategories(cancellationToken); + if (!categoryData.Success) + return new RefreshedPackageDataResponse() { Success = false, Exception = packagesResponse.Exception }; + + // Update the package data + refreshedPackage.Name = packageRefreshSource.Name; + refreshedPackage.Status = packageRefreshSource.Status; + var newCategory = categoryData.Categories.FirstOrDefault(x => x.Id.ToString() == packageRefreshSource.CategoryId); + refreshedPackage.Category = newCategory != null ? newCategory.Name : "Unknown"; + refreshedPackage.Modified = packageRefreshSource.Modified; + refreshedPackage.Size = packageRefreshSource.Size; + + return new RefreshedPackageDataResponse() { Success = true, Package = refreshedPackage }; + } + catch (OperationCanceledException) + { + return new RefreshedPackageDataResponse() { Success = false, Cancelled = true }; + } + catch (Exception e) + { + return new RefreshedPackageDataResponse() { Success = false, Exception = e }; + } + } + + public async Task GetPackageUploadedVersions(Package package, CancellationToken cancellationToken = default) + { + try + { + var uri = ApiUtility.CreateUri(Constants.Api.GetPackageUploadedVersionsUrl(package.PackageId, package.VersionId), true); + var response = await _client.Get(uri, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + response.EnsureSuccessStatusCode(); + + var responseStr = response.Content.ReadAsStringAsync().Result; + return new PackageUploadedUnityVersionDataResponse(responseStr); + } + catch (OperationCanceledException e) + { + return new PackageUploadedUnityVersionDataResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackageUploadedUnityVersionDataResponse() { Success = false, Exception = e }; + } + } + + public async Task UploadPackage(IPackageUploader uploader, IProgress progress = null, CancellationToken cancellationToken = default) + { + try + { + return await uploader.Upload(_client, progress, cancellationToken); + } + catch (OperationCanceledException e) + { + return new PackageUploadResponse() { Success = false, Cancelled = true, Exception = e }; + } + catch (Exception e) + { + return new PackageUploadResponse() { Success = false, Exception = e }; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs.meta new file mode 100644 index 0000000..3763396 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 684fca3fffd79d944a32d9b3adbfc007 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreApi.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs new file mode 100644 index 0000000..f71bff8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs @@ -0,0 +1,55 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class AssetStoreClient : IAssetStoreClient + { + private HttpClient _httpClient; + + public AssetStoreClient() + { + ServicePointManager.DefaultConnectionLimit = 500; + _httpClient = new HttpClient(); + _httpClient.DefaultRequestHeaders.ConnectionClose = false; + _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + _httpClient.Timeout = TimeSpan.FromMinutes(1320); + } + + public void SetSessionId(string sessionId) + { + ClearSessionId(); + + if (!string.IsNullOrEmpty(sessionId)) + _httpClient.DefaultRequestHeaders.Add("X-Unity-Session", sessionId); + } + + public void ClearSessionId() + { + _httpClient.DefaultRequestHeaders.Remove("X-Unity-Session"); + } + + public Task Get(Uri uri, CancellationToken cancellationToken = default) + { + return _httpClient.GetAsync(uri, cancellationToken); + } + + public Task Post(Uri uri, HttpContent content, CancellationToken cancellationToken = default) + { + return _httpClient.PostAsync(uri, content, cancellationToken); + } + + public Task Put(Uri uri, HttpContent content, CancellationToken cancellationToken = default) + { + return _httpClient.PutAsync(uri, content, cancellationToken); + } + + public Task Send(HttpRequestMessage request, CancellationToken cancellationToken = default) + { + return _httpClient.SendAsync(request, cancellationToken); + } + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs.meta new file mode 100644 index 0000000..10ea6ae --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 80b4527c908161a4b9f06dc393b502f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/AssetStoreClient.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs b/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs new file mode 100644 index 0000000..5a1bd5e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs @@ -0,0 +1,25 @@ +using AssetStoreTools.Api.Responses; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class CloudTokenAuthentication : AuthenticationBase + { + public CloudTokenAuthentication(string cloudToken) + { + AuthenticationContent = GetAuthenticationContent( + new KeyValuePair("user_access_token", cloudToken) + ); + } + + public override async Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken) + { + var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + return ParseResponse(result); + } + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs.meta new file mode 100644 index 0000000..06064fc --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 99f1baec74f26a34bb972b19c92d523f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/CloudTokenAuthentication.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs b/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs new file mode 100644 index 0000000..2c51505 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs @@ -0,0 +1,26 @@ +using AssetStoreTools.Api.Responses; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class CredentialsAuthentication : AuthenticationBase + { + public CredentialsAuthentication(string email, string password) + { + AuthenticationContent = GetAuthenticationContent( + new KeyValuePair("user", email), + new KeyValuePair("pass", password) + ); + } + + public override async Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken) + { + var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + return ParseResponse(result); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs.meta new file mode 100644 index 0000000..53e59db --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 353e556b63fd441428f387bc85aa612c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/CredentialsAuthentication.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Models.meta similarity index 77% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload.meta rename to Packages/com.unity.asset-store-tools/Editor/Api/Models.meta index f45530f..bfcab0b 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 854f6f9e93b37204eb2e6042138643bc +guid: 1f83e4b5507886f4b873c22c146b8f6a folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs new file mode 100644 index 0000000..2cb1298 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class Category + { + public int Id { get; set; } + public string Name { get; set; } + public string Status { get; set; } + + public class AssetStoreCategoryResolver : DefaultContractResolver + { + private Dictionary _propertyConversions; + + public AssetStoreCategoryResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(Category.Name), "assetstore_name" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + + public class CachedCategoryResolver : DefaultContractResolver + { + private static CachedCategoryResolver _instance; + public static CachedCategoryResolver Instance => _instance ?? (_instance = new CachedCategoryResolver()); + + private Dictionary _propertyConversion; + + private CachedCategoryResolver() + { + this.NamingStrategy = new SnakeCaseNamingStrategy(); + _propertyConversion = new Dictionary() + { + { nameof(Category.Name), "name" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversion.ContainsKey(propertyName)) + return _propertyConversion[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs.meta new file mode 100644 index 0000000..8a88ac8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5897866bc65f5834dab1f17371daada7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Models/Category.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs new file mode 100644 index 0000000..442c919 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs @@ -0,0 +1,80 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class Package + { + public string PackageId { get; set; } + public string VersionId { get; set; } + public string Name { get; set; } + public string Status { get; set; } + public string Category { get; set; } + public bool IsCompleteProject { get; set; } + public string RootGuid { get; set; } + public string RootPath { get; set; } + public string ProjectPath { get; set; } + public string Modified { get; set; } + public string Size { get; set; } + public string IconUrl { get; set; } + + public class AssetStorePackageResolver : DefaultContractResolver + { + private static AssetStorePackageResolver _instance; + public static AssetStorePackageResolver Instance => _instance ?? (_instance = new AssetStorePackageResolver()); + + private Dictionary _propertyConversions; + + private AssetStorePackageResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(Package.VersionId), "id" }, + { nameof(Package.IsCompleteProject), "is_complete_project" }, + { nameof(Package.RootGuid), "root_guid" }, + { nameof(Package.RootPath), "root_path" }, + { nameof(Package.ProjectPath), "project_path" }, + { nameof(Package.IconUrl), "icon_url" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + + public class CachedPackageResolver : DefaultContractResolver + { + private static CachedPackageResolver _instance; + public static CachedPackageResolver Instance => _instance ?? (_instance = new CachedPackageResolver()); + + private Dictionary _propertyConversion; + + private CachedPackageResolver() + { + this.NamingStrategy = new SnakeCaseNamingStrategy(); + _propertyConversion = new Dictionary() + { + { nameof(Package.PackageId), "package_id" }, + { nameof(Package.VersionId), "version_id" }, + { nameof(Package.IsCompleteProject), "is_complete_project" }, + { nameof(Package.RootGuid), "root_guid" }, + { nameof(Package.RootPath), "root_path" }, + { nameof(Package.IconUrl), "icon_url" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversion.ContainsKey(propertyName)) + return _propertyConversion[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs.meta new file mode 100644 index 0000000..79fb6c2 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7e9f0b99820061b49abf6e8cf544a727 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Models/Package.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs new file mode 100644 index 0000000..8686ccc --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class PackageAdditionalData + { + public string Name { get; set; } + public string Status { get; set; } + public string PackageId { get; set; } + public string VersionId { get; set; } + public string CategoryId { get; set; } + public string Modified { get; set; } + public string Size { get; set; } + + public class AssetStorePackageResolver : DefaultContractResolver + { + private static AssetStorePackageResolver _instance; + public static AssetStorePackageResolver Instance => _instance ?? (_instance = new AssetStorePackageResolver()); + + private Dictionary _propertyConversions; + + private AssetStorePackageResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(PackageAdditionalData.PackageId), "id" }, + { nameof(PackageAdditionalData.CategoryId), "category_id" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs.meta new file mode 100644 index 0000000..41f8b1f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0663f29f3fcd0e34ab77338d1bdbb528 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Models/PackageAdditionalData.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs new file mode 100644 index 0000000..c80a506 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Models +{ + internal class User + { + public string Id { get; set; } + public string SessionId { get; set; } + public string Name { get; set; } + public string Username { get; set; } + public string PublisherId { get; set; } + public bool IsPublisher => !string.IsNullOrEmpty(PublisherId); + + public override string ToString() + { + return + $"{nameof(Id)}: {Id}\n" + + $"{nameof(Name)}: {Name}\n" + + $"{nameof(Username)}: {Username}\n" + + $"{nameof(PublisherId)}: {PublisherId}\n" + + $"{nameof(IsPublisher)}: {IsPublisher}\n" + + $"{nameof(SessionId)}: [HIDDEN]"; + } + + public class AssetStoreUserResolver : DefaultContractResolver + { + private static AssetStoreUserResolver _instance; + public static AssetStoreUserResolver Instance => _instance ?? (_instance = new AssetStoreUserResolver()); + + private Dictionary _propertyConversions; + + private AssetStoreUserResolver() + { + _propertyConversions = new Dictionary() + { + { nameof(User.SessionId), "xunitysession" }, + { nameof(User.PublisherId), "publisher" } + }; + } + + protected override string ResolvePropertyName(string propertyName) + { + if (_propertyConversions.ContainsKey(propertyName)) + return _propertyConversions[propertyName]; + + return base.ResolvePropertyName(propertyName); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs.meta new file mode 100644 index 0000000..76244da --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: caf38df5cd685a345a1ebec8f7651c93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Models/User.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Workflows.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses.meta similarity index 77% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Workflows.meta rename to Packages/com.unity.asset-store-tools/Editor/Api/Responses.meta index d4b82df..534c529 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Workflows.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1c85e58f7d4786a40a140c67b0d124a0 +guid: 49581213e7b6ca645955cce8ce23ac4b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs new file mode 100644 index 0000000..2e7a6b2 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; + +namespace AssetStoreTools.Api.Responses +{ + /// + /// A structure used to return the success outcome and the result of Asset Store API calls + /// + internal class AssetStoreResponse + { + public bool Success { get; set; } = false; + public bool Cancelled { get; set; } = false; + public Exception Exception { get; set; } + + public AssetStoreResponse() { } + + public AssetStoreResponse(Exception e) : this() + { + Exception = e; + } + + protected void ValidateAssetStoreResponse(string json) + { + var dict = JsonConvert.DeserializeObject(json); + if (dict == null) + throw new Exception("Response is empty"); + + // Some json responses return an error field on error + if (dict.ContainsKey("error")) + { + // Server side error message + // Do not write to console since this is an error that + // is "expected" ie. can be handled by the gui. + throw new Exception(dict.GetValue("error").ToString()); + } + // Some json responses return status+message fields instead of an error field. Go figure. + else if (dict.ContainsKey("status") && dict.GetValue("status").ToString() != "ok" + && dict.ContainsKey("message")) + { + throw new Exception(dict.GetValue("message").ToString()); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs.meta new file mode 100644 index 0000000..11f75b6 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ee338db031a0cfb459f7cac7f41a5d75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs new file mode 100644 index 0000000..ec3bb87 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; + +namespace AssetStoreTools.Api.Responses +{ + internal class AssetStoreToolsVersionResponse : AssetStoreResponse + { + public string Version { get; set; } + + public AssetStoreToolsVersionResponse() : base() { } + public AssetStoreToolsVersionResponse(Exception e) : base(e) { } + + public AssetStoreToolsVersionResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseVersion(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseVersion(string json) + { + var dict = JsonConvert.DeserializeObject(json); + if (!dict.ContainsKey("version")) + throw new Exception("Version was not found"); + + Version = dict.GetValue("version").ToString(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs.meta new file mode 100644 index 0000000..64eb625 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 40558675926f913478a654350149209e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/AssetStoreToolsVersionResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs new file mode 100644 index 0000000..09c786c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs @@ -0,0 +1,74 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using System; +using System.Net; +using System.Net.Http; + +namespace AssetStoreTools.Api.Responses +{ + internal class AuthenticationResponse : AssetStoreResponse + { + public User User { get; set; } + + public AuthenticationResponse() : base() { } + + public AuthenticationResponse(Exception e) : base(e) { } + + public AuthenticationResponse(HttpStatusCode statusCode, HttpRequestException fallbackException) + { + string message; + switch (statusCode) + { + case HttpStatusCode.Unauthorized: + message = "Incorrect email and/or password. Please try again."; + break; + case HttpStatusCode.InternalServerError: + message = "Authentication request failed\nIf you were logging in with your Unity Cloud account, please make sure you are still logged in.\n" + + "This might also be caused by too many invalid login attempts - if that is the case, please try again later."; + break; + default: + Exception = fallbackException; + return; + } + + Exception = new Exception(message); + } + + public AuthenticationResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = User.AssetStoreUserResolver.Instance + }; + User = JsonConvert.DeserializeObject(json, serializerSettings); + ValidateLoginData(); + ValidatePublisher(); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ValidateLoginData() + { + if (string.IsNullOrEmpty(User.Id) + || string.IsNullOrEmpty(User.SessionId) + || string.IsNullOrEmpty(User.Name) + || string.IsNullOrEmpty(User.Username)) + throw new Exception("Could not parse the necessary publisher information from the response."); + } + + private void ValidatePublisher() + { + if (!User.IsPublisher) + throw new Exception($"Your Unity ID {User.Name} is not currently connected to a publisher account. " + + $"Please create a publisher profile."); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs.meta new file mode 100644 index 0000000..a37ebf9 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ec3a5cb59a7e78646b07f800d317874d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/AuthenticationResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs new file mode 100644 index 0000000..4ff740b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs @@ -0,0 +1,43 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class CategoryDataResponse : AssetStoreResponse + { + public List Categories { get; set; } + + public CategoryDataResponse() : base() { } + public CategoryDataResponse(Exception e) : base(e) { } + + public CategoryDataResponse(string json) + { + try + { + var categoryArray = JsonConvert.DeserializeObject(json); + + Categories = new List(); + var serializer = new JsonSerializer() + { + ContractResolver = new Category.AssetStoreCategoryResolver() + }; + + foreach (var categoryData in categoryArray) + { + var category = categoryData.ToObject(serializer); + Categories.Add(category); + } + + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs.meta new file mode 100644 index 0000000..9684d20 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e3789323453f1604286b436f77bdca97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/CategoryDataResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs new file mode 100644 index 0000000..f4e8ac9 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs @@ -0,0 +1,31 @@ +using System; +using UnityEngine; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackageThumbnailResponse : AssetStoreResponse + { + public Texture2D Thumbnail { get; set; } + public PackageThumbnailResponse() : base() { } + public PackageThumbnailResponse(Exception e) : base(e) { } + + public PackageThumbnailResponse(byte[] textureBytes) + { + try + { + var tex = new Texture2D(1, 1, TextureFormat.RGBA32, false); + var success = tex.LoadImage(textureBytes); + if (!success) + throw new Exception("Could not retrieve image from the provided texture bytes"); + + Thumbnail = tex; + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs.meta new file mode 100644 index 0000000..2f957f5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dacfba636b3757e408514b850d715e18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageThumbnailResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs new file mode 100644 index 0000000..4f57d64 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackageUploadedUnityVersionDataResponse : AssetStoreResponse + { + public List UnityVersions { get; set; } + + public PackageUploadedUnityVersionDataResponse() : base() { } + public PackageUploadedUnityVersionDataResponse(Exception e) : base(e) { } + + public PackageUploadedUnityVersionDataResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseVersionData(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseVersionData(string json) + { + var data = JsonConvert.DeserializeObject(json); + try + { + var content = data.GetValue("content").ToObject(); + UnityVersions = content.GetValue("unity_versions").ToObject>(); + } + catch + { + throw new Exception("Could not parse the unity versions array"); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs.meta new file mode 100644 index 0000000..fa263ee --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2552f659a600e124aa952f3ba760ddf3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackageUploadedUnityVersionDataResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs new file mode 100644 index 0000000..df01e70 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackagesAdditionalDataResponse : AssetStoreResponse + { + public List Packages { get; set; } + + public PackagesAdditionalDataResponse() : base() { } + public PackagesAdditionalDataResponse(Exception e) : base(e) { } + + public PackagesAdditionalDataResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseExtraData(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseExtraData(string json) + { + var packageDict = JsonConvert.DeserializeObject(json); + if (!packageDict.ContainsKey("packages")) + throw new Exception("Response did not not contain the list of packages"); + + Packages = new List(); + var serializer = new JsonSerializer() + { + ContractResolver = PackageAdditionalData.AssetStorePackageResolver.Instance + }; + + var packageArray = packageDict.GetValue("packages").ToObject(); + foreach (var packageData in packageArray) + { + var package = packageData.ToObject(serializer); + + // Some fields are based on the latest version in the json + var latestVersion = packageData["versions"].ToObject().Last; + + package.VersionId = latestVersion["id"].ToString(); + package.Modified = latestVersion["modified"].ToString(); + package.Size = latestVersion["size"].ToString(); + + Packages.Add(package); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs.meta new file mode 100644 index 0000000..0590efe --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 88d58ad5e0eea6345b5c83f30ee8ebd5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesAdditionalDataResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs new file mode 100644 index 0000000..d9fb47c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Api.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackagesDataResponse : AssetStoreResponse + { + public List Packages { get; set; } + + public PackagesDataResponse() : base() { } + public PackagesDataResponse(Exception e) : base(e) { } + + public PackagesDataResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + ParseMainData(json); + Success = true; + } + catch (Exception e) + { + Success = false; + Exception = e; + } + } + + private void ParseMainData(string json) + { + var packageDict = JsonConvert.DeserializeObject(json); + if (!packageDict.ContainsKey("packages")) + throw new Exception("Response did not not contain the list of packages"); + + Packages = new List(); + var serializer = new JsonSerializer() + { + ContractResolver = Package.AssetStorePackageResolver.Instance + }; + + foreach (var packageToken in packageDict["packages"]) + { + var property = (JProperty)packageToken; + var packageData = property.Value.ToObject(serializer); + + // Package Id is the key of the package object + packageData.PackageId = property.Name; + + // Package Icon Url is returned without the https: prefix + if (!string.IsNullOrEmpty(packageData.IconUrl)) + packageData.IconUrl = $"https:{packageData.IconUrl}"; + + Packages.Add(packageData); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs.meta new file mode 100644 index 0000000..f780da1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 705ec748e689148479f54666993cd79d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/PackagesDataResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs new file mode 100644 index 0000000..a0479a0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Api.Models; +using System; + +namespace AssetStoreTools.Api.Responses +{ + internal class RefreshedPackageDataResponse : AssetStoreResponse + { + public Package Package { get; set; } + public RefreshedPackageDataResponse() { } + public RefreshedPackageDataResponse(Exception e) : base(e) { } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs.meta new file mode 100644 index 0000000..0607505 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 20f710024d5ed514db02672f12ac361c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/RefreshedPackageDataResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs new file mode 100644 index 0000000..3fa2160 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs @@ -0,0 +1,28 @@ +using System; + +namespace AssetStoreTools.Api.Responses +{ + internal class PackageUploadResponse : AssetStoreResponse + { + public UploadStatus Status { get; set; } + + public PackageUploadResponse() : base() { } + public PackageUploadResponse(Exception e) : base(e) { } + + public PackageUploadResponse(string json) + { + try + { + ValidateAssetStoreResponse(json); + Status = UploadStatus.Success; + Success = true; + } + catch (Exception e) + { + Success = false; + Status = UploadStatus.Fail; + Exception = e; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs.meta new file mode 100644 index 0000000..8b61970 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8f1758cfa8119cf49a61b010a04352e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/Responses/UploadResponse.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs b/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs new file mode 100644 index 0000000..eb5e54c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs @@ -0,0 +1,25 @@ +using AssetStoreTools.Api.Responses; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class SessionAuthentication : AuthenticationBase + { + public SessionAuthentication(string sessionId) + { + AuthenticationContent = GetAuthenticationContent( + new KeyValuePair("reuse_session", sessionId) + ); + } + + public override async Task Authenticate(IAssetStoreClient client, CancellationToken cancellationToken) + { + var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + + return ParseResponse(result); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs.meta new file mode 100644 index 0000000..ca64f18 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 02e970f660088cc4b89003608d59cd06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/SessionAuthentication.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs b/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs new file mode 100644 index 0000000..17c765f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs @@ -0,0 +1,99 @@ +using AssetStoreTools.Api.Responses; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Api +{ + internal class UnityPackageUploadSettings + { + public string VersionId { get; set; } + public string UnityPackagePath { get; set; } + public string RootGuid { get; set; } + public string RootPath { get; set; } + public string ProjectPath { get; set; } + } + + internal class UnityPackageUploader : PackageUploaderBase + { + private UnityPackageUploadSettings _settings; + private Uri _uploadUri; + + public UnityPackageUploader(UnityPackageUploadSettings settings) + { + _settings = settings; + } + + protected override void ValidateSettings() + { + if (string.IsNullOrEmpty(_settings.VersionId)) + throw new Exception("Version Id is unset"); + + if (string.IsNullOrEmpty(_settings.UnityPackagePath) + || !File.Exists(_settings.UnityPackagePath)) + throw new Exception("Package file could not be found"); + + if (!_settings.UnityPackagePath.EndsWith(".unitypackage")) + throw new Exception("Provided package file is not .unitypackage"); + } + + public override async Task Upload(IAssetStoreClient client, IProgress progress = null, CancellationToken cancellationToken = default) + { + try + { + ValidateSettings(); + + var endpoint = Constants.Api.UploadUnityPackageUrl(_settings.VersionId); + var query = new Dictionary() + { + { "root_guid", _settings.RootGuid }, + { "root_path", _settings.RootPath }, + { "project_path", _settings.ProjectPath } + }; + + _uploadUri = ApiUtility.CreateUri(endpoint, query, true); + } + catch (Exception e) + { + return new PackageUploadResponse() { Success = false, Status = UploadStatus.Fail, Exception = e }; + } + + return await Task.Run(() => UploadTask(client, progress, cancellationToken)); + } + + private PackageUploadResponse UploadTask(IAssetStoreClient client, IProgress progress, CancellationToken cancellationToken) + { + try + { + using (FileStream requestFileStream = new FileStream(_settings.UnityPackagePath, FileMode.Open, FileAccess.Read)) + { + var content = new StreamContent(requestFileStream, UploadChunkSizeBytes); + + var response = client.Put(_uploadUri, content, cancellationToken); + WaitForUploadCompletion(response, requestFileStream, progress, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + EnsureSuccessResponse(response.Result); + + var responseStr = response.Result.Content.ReadAsStringAsync().Result; + return new PackageUploadResponse(responseStr); + } + } + catch (OperationCanceledException e) + { + return new PackageUploadResponse() { Success = false, Cancelled = true, Status = UploadStatus.Cancelled, Exception = e }; + } + catch (TimeoutException e) + { + return new PackageUploadResponse() { Success = true, Status = UploadStatus.ResponseTimeout, Exception = e }; + } + catch (Exception e) + { + return new PackageUploadResponse() { Success = false, Exception = e, Status = UploadStatus.Fail }; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs.meta new file mode 100644 index 0000000..cdc262a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 95bd0284375397f41a2065e07d4ba526 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/UnityPackageUploader.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs b/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs new file mode 100644 index 0000000..cc3adf4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs @@ -0,0 +1,11 @@ +namespace AssetStoreTools.Api +{ + internal enum UploadStatus + { + Default = 0, + Success = 1, + Fail = 2, + Cancelled = 3, + ResponseTimeout = 4 + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs.meta new file mode 100644 index 0000000..83a0310 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0a121831a941cb64a8a9d21ca6fda9c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Api/UploadStatus.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs b/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs index 202cc46..81fbd08 100644 --- a/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs +++ b/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Unity.AssetStoreTools.Editor.Tests.asmdef")] +[assembly: InternalsVisibleTo("AssetStoreTools.Tests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: InternalsVisibleTo("ab-builder")] [assembly: InternalsVisibleTo("Inspector-Editor")] diff --git a/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta b/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta index f74c22d..e3d24b4 100644 --- a/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs.meta @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 + packageVersion: 12.0.1 assetPath: Packages/com.unity.asset-store-tools/Editor/AssemblyInfo.cs - uploadId: 712972 + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs b/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs index 3c00185..373ac32 100644 --- a/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs +++ b/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs @@ -1,31 +1,53 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.UI; +using AssetStoreTools.Uploader; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator.Data; +using AssetStoreTools.Validator.UI; +using System; using UnityEditor; using UnityEngine; -using System; -using AssetStoreTools.Uploader; -using AssetStoreTools.Validator; -using AssetStoreTools.Utility; namespace AssetStoreTools { - internal class AssetStoreTools : EditorWindow + internal static class AssetStoreTools { [MenuItem("Tools/Asset Store/Uploader", false, 0)] public static void ShowAssetStoreToolsUploader() { Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); - var wnd = GetWindow(inspectorType); + var wnd = EditorWindow.GetWindow(inspectorType); wnd.Show(); } - - + [MenuItem("Tools/Asset Store/Validator", false, 1)] public static void ShowAssetStoreToolsValidator() { Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); - var wnd = GetWindow(typeof(AssetStoreUploader), inspectorType); + var wnd = EditorWindow.GetWindow(typeof(UploaderWindow), inspectorType); wnd.Show(); } + public static void ShowAssetStoreToolsValidator(ValidationSettings settings, ValidationResult result) + { + ShowAssetStoreToolsValidator(); + EditorWindow.GetWindow().Load(settings, result); + } + + [MenuItem("Tools/Asset Store/Preview Generator", false, 2)] + public static void ShowAssetStoreToolsPreviewGenerator() + { + Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll"); + var wnd = EditorWindow.GetWindow(inspectorType); + wnd.Show(); + } + + public static void ShowAssetStoreToolsPreviewGenerator(PreviewGenerationSettings settings) + { + ShowAssetStoreToolsPreviewGenerator(); + EditorWindow.GetWindow().Load(settings); + } + [MenuItem("Tools/Asset Store/Publisher Portal", false, 20)] public static void OpenPublisherPortal() { @@ -47,7 +69,7 @@ namespace AssetStoreTools [MenuItem("Tools/Asset Store/Check for Updates", false, 45)] public static void OpenUpdateChecker() { - var wnd = GetWindowWithRect(new Rect(Screen.width / 2, Screen.height / 2, 400, 150), true); + var wnd = EditorWindow.GetWindowWithRect(new Rect(Screen.width / 2, Screen.height / 2, 400, 150), true); wnd.Show(); } diff --git a/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta b/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta index fec1469..1d99f81 100644 --- a/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs.meta @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 + packageVersion: 12.0.1 assetPath: Packages/com.unity.asset-store-tools/Editor/AssetStoreTools.cs - uploadId: 712972 + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs b/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs index aa69025..43f3c11 100644 --- a/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs +++ b/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs @@ -7,15 +7,17 @@ namespace AssetStoreTools { protected abstract string WindowTitle { get; } - protected virtual void Init() + private void DefaultInit() { titleContent = new GUIContent(WindowTitle); + Init(); } + protected abstract void Init(); + private void OnEnable() { - Init(); + DefaultInit(); } - } } \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta b/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta index c91fb0f..2dc3a12 100644 --- a/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs.meta @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 + packageVersion: 12.0.1 assetPath: Packages/com.unity.asset-store-tools/Editor/AssetStoreToolsWindow.cs - uploadId: 712972 + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Constants.cs b/Packages/com.unity.asset-store-tools/Editor/Constants.cs new file mode 100644 index 0000000..d2c777a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Constants.cs @@ -0,0 +1,178 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; + +namespace AssetStoreTools +{ + internal class Constants + { +#if UNITY_EDITOR_OSX + public static readonly string UnityPath = System.IO.Path.Combine(EditorApplication.applicationPath, "Contents", "MacOS", "Unity"); +#else + public static readonly string UnityPath = EditorApplication.applicationPath; +#endif + public static readonly string RootProjectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length); + + private static bool GetArgument(string argumentName, out string argumentValue) + { + argumentValue = string.Empty; + var args = Environment.GetCommandLineArgs(); + + for (int i = 0; i < args.Length; i++) + { + if (!args[i].Equals(argumentName, StringComparison.OrdinalIgnoreCase)) + continue; + + if (i + 1 >= args.Length) + return false; + + argumentValue = args[i + 1]; + break; + } + + return !string.IsNullOrEmpty(argumentValue); + } + + public class Api + { + public static readonly string ApiVersion = $"V{PackageInfo.FindForAssetPath("Packages/com.unity.asset-store-tools").version}"; + public const string AssetStoreToolsLatestVersionUrl = "https://api.assetstore.unity3d.com/package/latest-version/115"; + + private const string AssetStoreBaseUrlDefault = "https://kharma.unity3d.com"; + private const string AssetStoreBaseUrlOverrideArgument = "-assetStoreUrl"; + public static readonly string AssetStoreBaseUrl = !GetArgument(AssetStoreBaseUrlOverrideArgument, out var overriddenUrl) + ? AssetStoreBaseUrlDefault + : overriddenUrl; + + public static readonly string AuthenticateUrl = $"{AssetStoreBaseUrl}/login"; + public static readonly string GetPackagesUrl = $"{AssetStoreBaseUrl}/api/asset-store-tools/metadata/0.json"; + public static readonly string GetPackagesAdditionalDataUrl = $"{AssetStoreBaseUrl}/api/management/packages.json"; + public static readonly string GetCategoriesUrl = $"{AssetStoreBaseUrl}/api/management/categories.json"; + + public static string GetPackageUploadedVersionsUrl(string packageId, string versionId) => + $"{AssetStoreBaseUrl}/api/content/preview/{packageId}/{versionId}.json"; + public static string UploadUnityPackageUrl(string versionId) => + $"{AssetStoreBaseUrl}/api/asset-store-tools/package/{versionId}/unitypackage.json"; + + public static IDictionary DefaultAssetStoreQuery() + { + var dict = new Dictionary() + { + { "unityversion", Application.unityVersion }, + { "toolversion", ApiVersion } + }; + return dict; + } + } + + public class Updater + { + public const string AssetStoreToolsUrl = "https://assetstore.unity.com/packages/tools/utilities/asset-store-publishing-tools-115"; + } + + public class Cache + { + public const string SessionTokenKey = "kharma.sessionid"; + public const string TempCachePath = "Temp/AssetStoreToolsCache"; + public const string PersistentCachePath = "Library/AssetStoreToolsCache"; + + public const string PackageDataFileName = "PackageMetadata.json"; + public const string CategoryDataFile = "Categories.json"; + public const string ValidationResultFile = "ValidationStateData.asset"; + + public static string PackageThumbnailFileName(string packageId) => $"{packageId}.png"; + public static string WorkflowStateDataFileName(string packageId) => $"{packageId}-workflowStateData.asset"; + } + + public class Uploader + { + public const string MinRequiredUnitySupportVersion = "2021.3"; + public const long MaxPackageSizeBytes = 6576668672; // 6 GB + 128MB of headroom + public const string AccountRegistrationUrl = "https://publisher.unity.com/access"; + public const string AccountForgottenPasswordUrl = "https://id.unity.com/password/new"; + + public class Analytics + { + public const string VendorKey = "unity.assetStoreTools"; + public const int MaxEventsPerHour = 20; + public const int MaxNumberOfElements = 1000; + + public class AuthenticationAnalytics + { + public const string EventName = "assetStoreToolsLogin"; + public const int EventVersion = 1; + } + + public class PackageUploadAnalytics + { + public const string EventName = "assetStoreTools"; + public const int EventVersion = 3; + } + } + } + + public class Validator + { + public const string SubmissionGuidelinesUrl = "https://assetstore.unity.com/publishing/submission-guidelines#Overview"; + public const string SupportTicketUrl = "https://support.unity.com/hc/en-us/requests/new?ticket_form_id=65905"; + + public class Tests + { + public const string TestDefinitionsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Tests"; + public const string TestMethodsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Test Methods"; + + public static readonly string GenericTestMethodsPath = $"{TestMethodsPath}/Generic"; + public static readonly string UnityPackageTestMethodsPath = $"{TestMethodsPath}/UnityPackage"; + } + } + + public class Previews + { + public const string PreviewDatabaseFile = "PreviewDatabase.json"; + + public static readonly string DefaultOutputPath = $"{Cache.TempCachePath}/AssetPreviews"; + public const FileNameFormat DefaultFileNameFormat = FileNameFormat.Guid; + + public class Native + { + public static readonly string DefaultOutputPath = $"{Previews.DefaultOutputPath}/Native"; + public const PreviewFormat DefaultFormat = PreviewFormat.PNG; + public const bool DefaultWaitForPreviews = true; + public const bool DefaultChunkedPreviewLoading = true; + public const int DefaultChunkSize = 100; + } + + public class Custom + { + public static readonly string DefaultOutputPath = $"{Previews.DefaultOutputPath}/Custom"; + public const PreviewFormat DefaultFormat = PreviewFormat.JPG; + public const int DefaultWidth = 300; + public const int DefaultHeight = 300; + public const int DefaultDepth = 32; + + public const int DefaultNativeWidth = 900; + public const int DefaultNativeHeight = 900; + + public static readonly Color DefaultAudioSampleColor = new Color(1f, 0.55f, 0); + public static readonly Color DefaultAudioBackgroundColor = new Color(0.32f, 0.32f, 0.32f); + } + } + + public class WindowStyles + { + public const string UploaderStylesPath = "Packages/com.unity.asset-store-tools/Editor/Uploader/Styles"; + public const string ValidatorStylesPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Styles"; + public const string ValidatorIconsPath = "Packages/com.unity.asset-store-tools/Editor/Validator/Icons"; + public const string PreviewGeneratorStylesPath = "Packages/com.unity.asset-store-tools/Editor/Previews/Styles"; + public const string UpdaterStylesPath = "Packages/com.unity.asset-store-tools/Editor/Utility/Styles/Updater"; + } + + public class Debug + { + public const string DebugModeKey = "ASTDebugMode"; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Constants.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Constants.cs.meta new file mode 100644 index 0000000..9fa0657 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Constants.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a4ee1f78505bda748ae24b3dfd2606ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Constants.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions.meta new file mode 100644 index 0000000..ac18e6c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2072d354c04b19c48b22593536b3ebcf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs new file mode 100644 index 0000000..0201a8e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace AssetStoreTools.Exporter +{ + internal interface IPackageExporter + { + PackageExporterSettings Settings { get; } + + Task Export(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs.meta new file mode 100644 index 0000000..2285d4b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 41bc3a111ed1fd64c8b9acef211d9e51 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPackageExporter.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs new file mode 100644 index 0000000..ba503d8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Exporter +{ + internal interface IPreviewInjector + { + void Inject(string temporaryPackagePath); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs.meta new file mode 100644 index 0000000..7f4182e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dcff58dc716351f43b2709cfacdfebba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/IPreviewInjector.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs similarity index 54% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs rename to Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs index efa68c9..885a188 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs @@ -8,47 +8,51 @@ using UnityEditor; namespace AssetStoreTools.Exporter { - internal abstract class PackageExporter + internal abstract class PackageExporterBase : IPackageExporter { - protected const string ProgressBarTitle = "Exporting Package"; - protected const string ProgressBarStepSavingAssets = "Saving Assets..."; - protected const string ProgressBarStepGatheringFiles = "Gathering files..."; - protected const string ProgressBarStepCompressingPackage = "Compressing package..."; + public PackageExporterSettings Settings { get; private set; } + + public const string ProgressBarTitle = "Exporting Package"; + public const string ProgressBarStepSavingAssets = "Saving Assets..."; + public const string ProgressBarStepGatheringFiles = "Gathering files..."; + public const string ProgressBarStepGeneratingPreviews = "Generating previews..."; + public const string ProgressBarStepCompressingPackage = "Compressing package..."; private static readonly string[] PluginFolderExtensions = { "androidlib", "bundle", "plugin", "framework", "xcframework" }; - public static async Task ExportPackage(ExporterSettings exportSettings) + public PackageExporterBase(PackageExporterSettings settings) { - if (!IsSettingsValid(exportSettings, out Exception e)) - return new ExportResult() { Success = false, Error = ASError.GetGenericError(e) }; + Settings = settings; + } - switch (exportSettings) + public async Task Export() + { + try { - case LegacyExporterSettings legacySettings: - return await PackageExporterLegacy.ExportPackage(legacySettings); - case DefaultExporterSettings defaultSettings: - return PackageExporterDefault.ExportPackage(defaultSettings); - default: - return new ExportResult() { Success = false, Error = ASError.GetGenericError(new ArgumentException("Unrecognized ExportSettings type was provided")) }; + ValidateSettings(); } + catch (Exception e) + { + return new PackageExporterResult() { Success = false, Exception = e }; + } + + return await ExportImpl(); } - private static bool IsSettingsValid(ExporterSettings settings, out Exception e) + protected virtual void ValidateSettings() { - e = null; + if (Settings == null) + throw new ArgumentException("Settings cannot be null"); - if (settings == null) - e = new ArgumentException("Package Exporting failed: ExporterSettings cannot be null"); - else if (settings.ExportPaths == null || settings.ExportPaths.Length == 0) - e = new ArgumentException("Package Exporting failed: received an invalid export paths array"); - else if (string.IsNullOrEmpty(settings.OutputFilename)) - e = new ArgumentException("Package Exporting failed: received an invalid output path"); - else if (settings.OutputFilename.EndsWith("/") || settings.OutputFilename.EndsWith("\\")) - e = new ArgumentException("Package Exporting failed: output path must be a valid filename and not end with a directory separator character"); + if (string.IsNullOrEmpty(Settings.OutputFilename)) + throw new ArgumentException("Output path cannot be null"); - return e == null; + if (Settings.OutputFilename.EndsWith("/") || Settings.OutputFilename.EndsWith("\\")) + throw new ArgumentException("Output path must be a valid filename and not end with a directory separator character"); } + protected abstract Task ExportImpl(); + protected string[] GetAssetPaths(string rootPath) { // To-do: slight optimization is possible in the future by having a list of excluded folders/file extensions @@ -70,19 +74,9 @@ namespace AssetStoreTools.Exporter return paths.ToArray(); } - protected string GetAssetGuid(string assetPath, bool generateForPlugin, bool hiddenSearch) + protected string GetAssetGuid(string assetPath, bool generateIfPlugin, bool scrapeFromMeta) { - // Skip meta files as they do not have guids - if (assetPath.EndsWith(".meta", StringComparison.OrdinalIgnoreCase)) - return string.Empty; - - // Skip hidden assets. They normally do not have meta files, but - // have been observed to retain them in the past due to a Unity bug - if (assetPath.EndsWith("~")) - return string.Empty; - - var assetName = assetPath.Split('/').Last(); - if (assetName.StartsWith(".")) + if (!FileUtility.ShouldHaveMeta(assetPath)) return string.Empty; // Skip ProjectVersion.txt file specifically as it may introduce @@ -97,16 +91,15 @@ namespace AssetStoreTools.Exporter // Some special folders (e.g. SomeName.framework) do not have meta files inside them. // Their contents should be exported with any arbitrary GUID so that Unity Importer could pick them up - if (generateForPlugin && PathBelongsToPlugin(assetPath)) + if (generateIfPlugin && PathBelongsToPlugin(assetPath)) return GUID.Generate().ToString(); // Files in hidden folders (e.g. Samples~) are not part of the Asset Database, // therefore GUIDs need to be scraped from the .meta file. - // Note: only do this for custom exporter since the native exporter + // Note: only do this for non-native exporter since the native exporter // will not be able to retrieve the asset path from a hidden folder - if (hiddenSearch) + if (scrapeFromMeta) { - // To-do: handle hidden folders without meta files var metaPath = $"{assetPath}.meta"; if (!File.Exists(metaPath)) diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs.meta new file mode 100644 index 0000000..da3072c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aab20a0b596e60b40b1f7f7e0f54858e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs new file mode 100644 index 0000000..6197ba5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs @@ -0,0 +1,7 @@ +namespace AssetStoreTools.Exporter +{ + internal abstract class PackageExporterSettings + { + public string OutputFilename; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs.meta new file mode 100644 index 0000000..d6041f4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 82c350daaabdc784e95e09cdc8511e23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/Abstractions/PackageExporterSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs new file mode 100644 index 0000000..82a10ac --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Previews.Generators; + +namespace AssetStoreTools.Exporter +{ + internal class DefaultExporterSettings : PackageExporterSettings + { + public string[] ExportPaths; + public string[] Dependencies; + public IPreviewGenerator PreviewGenerator; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs.meta new file mode 100644 index 0000000..4f784d2 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 92cbd0e60b4bb9049bcf1e9fd92ccae6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultExporterSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs similarity index 66% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs rename to Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs index f2e8c7c..c8d6c42 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs @@ -1,35 +1,43 @@ -using AssetStoreTools.Uploader.Utility; +using AssetStoreTools.Previews.Data; using AssetStoreTools.Utility; -using AssetStoreTools.Utility.Json; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; +using System.Threading.Tasks; using UnityEditor; using UnityEngine; +using CacheConstants = AssetStoreTools.Constants.Cache; namespace AssetStoreTools.Exporter { - internal class PackageExporterDefault : PackageExporter + internal class DefaultPackageExporter : PackageExporterBase { private const string TemporaryExportPathName = "CustomExport"; - private DefaultExporterSettings _exportSettings; + private DefaultExporterSettings _defaultExportSettings; - private PackageExporterDefault(DefaultExporterSettings exportSettings) + public DefaultPackageExporter(DefaultExporterSettings settings) : base(settings) { - _exportSettings = exportSettings; + _defaultExportSettings = settings; } - public static ExportResult ExportPackage(DefaultExporterSettings exportSettings) + protected override void ValidateSettings() { - var exporter = new PackageExporterDefault(exportSettings); - return exporter.ExportPackage(); + base.ValidateSettings(); + + if (_defaultExportSettings.ExportPaths == null || _defaultExportSettings.ExportPaths.Length == 0) + throw new ArgumentException("Export paths array cannot be empty"); } - private ExportResult ExportPackage() + protected override async Task ExportImpl() + { + return await this.Export(); + } + + private new async Task Export() { ASDebug.Log("Using custom package exporter"); @@ -40,6 +48,7 @@ namespace AssetStoreTools.Exporter try { // Create a temporary export path + PostExportCleanup(); var temporaryExportPath = GetTemporaryExportPath(); if (!Directory.Exists(temporaryExportPath)) Directory.CreateDirectory(temporaryExportPath); @@ -47,17 +56,20 @@ namespace AssetStoreTools.Exporter // Construct an unzipped package structure CreateTempPackageStructure(temporaryExportPath); + var previewGenerationResult = await GeneratePreviews(); + InjectPreviews(previewGenerationResult, temporaryExportPath); + // Build a .unitypackage file from the temporary folder - CreateUnityPackage(temporaryExportPath, _exportSettings.OutputFilename); + CreateUnityPackage(temporaryExportPath, _defaultExportSettings.OutputFilename); - EditorUtility.RevealInFinder(_exportSettings.OutputFilename); + EditorUtility.RevealInFinder(_defaultExportSettings.OutputFilename); - ASDebug.Log($"Package file has been created at {_exportSettings.OutputFilename}"); - return new ExportResult() { Success = true, ExportedPath = _exportSettings.OutputFilename }; + ASDebug.Log($"Package file has been created at {_defaultExportSettings.OutputFilename}"); + return new PackageExporterResult() { Success = true, ExportedPath = _defaultExportSettings.OutputFilename, PreviewGenerationResult = previewGenerationResult }; } catch (Exception e) { - return new ExportResult() { Success = false, Error = ASError.GetGenericError(e) }; + return new PackageExporterResult() { Success = false, Exception = e }; } finally { @@ -67,19 +79,13 @@ namespace AssetStoreTools.Exporter private string GetTemporaryExportPath() { - return $"{AssetStoreCache.TempCachePath}/{TemporaryExportPathName}"; + return $"{CacheConstants.TempCachePath}/{TemporaryExportPathName}"; } private void CreateTempPackageStructure(string tempOutputPath) { EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepGatheringFiles, 0.4f); - var pathGuidPairs = GetPathGuidPairs(_exportSettings.ExportPaths); - - // Caching asset previews takes time, so we'll start doing it as we - // iterate through assets and only retrieve them after generating the rest - // of the package structure - AssetPreview.SetPreviewTextureCacheSize(pathGuidPairs.Count + 100); - var pathObjectPairs = new Dictionary(); + var pathGuidPairs = GetPathGuidPairs(_defaultExportSettings.ExportPaths); foreach (var pair in pathGuidPairs) { @@ -112,20 +118,15 @@ namespace AssetStoreTools.Exporter var previewObject = AssetDatabase.LoadAssetAtPath(originalAssetPath); if (previewObject == null) continue; - // Start caching the asset preview - AssetPreview.GetAssetPreview(previewObject); - pathObjectPairs.Add(outputAssetPath, previewObject); } - WritePreviewTextures(pathObjectPairs); - - if (_exportSettings.Dependencies == null || _exportSettings.Dependencies.Length == 0) + if (_defaultExportSettings.Dependencies == null || _defaultExportSettings.Dependencies.Length == 0) return; - var exportDependenciesDict = JsonValue.NewDict(); + var exportDependenciesDict = new JObject(); var allRegistryPackages = PackageUtility.GetAllRegistryPackages(); - - foreach(var exportDependency in _exportSettings.Dependencies) + + foreach (var exportDependency in _defaultExportSettings.Dependencies) { var registryPackage = allRegistryPackages.FirstOrDefault(x => x.name == exportDependency); if (registryPackage == null) @@ -139,7 +140,10 @@ namespace AssetStoreTools.Exporter exportDependenciesDict[registryPackage.name] = registryPackage.version; } - var exportManifestJson = JsonValue.NewDict(); + if (exportDependenciesDict.Count == 0) + return; + + var exportManifestJson = new JObject(); exportManifestJson["dependencies"] = exportDependenciesDict; var tempManifestDirectoryPath = $"{tempOutputPath}/packagemanagermanifest"; @@ -170,64 +174,36 @@ namespace AssetStoreTools.Exporter return pathGuidPairs; } - private void WritePreviewTextures(Dictionary pathObjectPairs) + private async Task GeneratePreviews() { - foreach (var kvp in pathObjectPairs) + if (_defaultExportSettings.PreviewGenerator == null) + return null; + + void ReportProgress(float progress) { - var obj = kvp.Value; - var queuePreview = false; - - switch (obj) - { - case Material _: - case TerrainLayer _: - case AudioClip _: - case Mesh _: - case Texture _: - case UnityEngine.Tilemaps.Tile _: - case GameObject _: - queuePreview = true; - break; - } - - if (!queuePreview) - continue; - - AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _); - var preview = GetAssetPreviewFromGuid(guid); - - if (!preview) - continue; - - var thumbnailWidth = Mathf.Min(preview.width, 128); - var thumbnailHeight = Mathf.Min(preview.height, 128); - var rt = RenderTexture.GetTemporary(thumbnailWidth, thumbnailHeight, 0, RenderTextureFormat.Default, RenderTextureReadWrite.sRGB); - - var copy = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false); - - RenderTexture.active = rt; - GL.Clear(true, true, new Color(0, 0, 0, 0)); - Graphics.Blit(preview, rt); - copy.ReadPixels(new Rect(0, 0, copy.width, copy.height), 0, 0, false); - copy.Apply(); - RenderTexture.active = null; - - var bytes = copy.EncodeToPNG(); - if (bytes != null && bytes.Length > 0) - { - File.WriteAllBytes(kvp.Key + "/preview.png", bytes); - } - - RenderTexture.ReleaseTemporary(rt); + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepGeneratingPreviews, progress); } + + _defaultExportSettings.PreviewGenerator.OnProgressChanged += ReportProgress; + var result = await _defaultExportSettings.PreviewGenerator.Generate(); + _defaultExportSettings.PreviewGenerator.OnProgressChanged -= ReportProgress; + EditorUtility.ClearProgressBar(); + + if (!result.Success) + { + UnityEngine.Debug.LogWarning($"An error was encountered while generating previews. Exported package may be missing previews.\n{result.Exception}"); + } + + return result; } - private Texture2D GetAssetPreviewFromGuid(string guid) + private void InjectPreviews(PreviewGenerationResult result, string temporaryExportPath) { - var method = typeof(AssetPreview).GetMethod("GetAssetPreviewFromGUID", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(string) }, null); - var args = new object[] { guid }; + if (result == null || !result.Success) + return; - return method?.Invoke(null, args) as Texture2D; + var injector = new PreviewInjector(result); + injector.Inject(temporaryExportPath); } private void CreateUnityPackage(string pathToArchive, string outputPath) @@ -235,7 +211,7 @@ namespace AssetStoreTools.Exporter if (Directory.GetDirectories(pathToArchive).Length == 0) throw new InvalidOperationException("Unable to export package. The specified path is empty"); - EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepCompressingPackage, 0.5f); + EditorUtility.DisplayProgressBar(ProgressBarTitle, ProgressBarStepCompressingPackage, 0.8f); // Archiving process working path will be set to the // temporary package path so adjust the output path accordingly @@ -245,7 +221,7 @@ namespace AssetStoreTools.Exporter #if UNITY_EDITOR_WIN CreateUnityPackageUniversal(pathToArchive, outputPath); #elif UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX - CreateUnityPackageOsxLinux(pathToArchive, outputPath); + CreateUnityPackageOsxLinux(pathToArchive, outputPath); #endif } @@ -272,6 +248,7 @@ namespace AssetStoreTools.Exporter throw new Exception("Failed to compress the package"); } +#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX private void CreateUnityPackageOsxLinux(string pathToArchive, string outputPath) { var tarPath = "/usr/bin/tar"; @@ -290,6 +267,7 @@ namespace AssetStoreTools.Exporter if (result != 0) throw new Exception("Failed to compress the package"); } +#endif private int StartProcess(string processPath, string arguments, string workingDirectory) { diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs.meta similarity index 84% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs.meta index feb2d56..7623320 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs.meta @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterDefault.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/DefaultPackageExporter.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs deleted file mode 100644 index 0ce4a73..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -using AssetStoreTools.Utility; - -namespace AssetStoreTools.Exporter -{ - internal class ExportResult - { - public bool Success; - public string ExportedPath; - public ASError Error; - - public static implicit operator bool(ExportResult value) - { - return value != null && value.Success; - } - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs deleted file mode 100644 index 0992bc8..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace AssetStoreTools.Exporter -{ - public abstract class ExporterSettings - { - public string[] ExportPaths; - public string OutputFilename; - } - - public class DefaultExporterSettings : ExporterSettings - { - public string[] Dependencies; - } - - public class LegacyExporterSettings : ExporterSettings - { - public bool IncludeDependencies; - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs new file mode 100644 index 0000000..52a7cad --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Exporter +{ + internal class LegacyExporterSettings : PackageExporterSettings + { + public string[] ExportPaths; + public bool IncludeDependencies; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs.meta similarity index 76% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs.meta index 1d5d5c4..e87e08c 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 399b115514c617d47a00b8c0a5e430fd +guid: c7dea1cfe45989e4eab6fc5fd18c1e10 MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/ExporterSettings.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyExporterSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs similarity index 68% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs rename to Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs index e6e1567..1720043 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs @@ -8,37 +8,44 @@ using System.Threading.Tasks; namespace AssetStoreTools.Exporter { - internal class PackageExporterLegacy : PackageExporter + internal class LegacyPackageExporter : PackageExporterBase { private const string ExportMethodWithoutDependencies = "UnityEditor.PackageUtility.ExportPackage"; private const string ExportMethodWithDependencies = "UnityEditor.PackageUtility.ExportPackageAndPackageManagerManifest"; - private LegacyExporterSettings _exportSettings; + private LegacyExporterSettings _legacyExportSettings; - private PackageExporterLegacy(LegacyExporterSettings exportSettings) + public LegacyPackageExporter(LegacyExporterSettings settings) : base(settings) { - _exportSettings = exportSettings; + _legacyExportSettings = settings; } - public static async Task ExportPackage(LegacyExporterSettings exportSettings) + protected override void ValidateSettings() { - var exporter = new PackageExporterLegacy(exportSettings); - return await exporter.ExportPackage(); + base.ValidateSettings(); + + if (_legacyExportSettings.ExportPaths == null || _legacyExportSettings.ExportPaths.Length == 0) + throw new ArgumentException("Export paths array cannot be empty"); } - private async Task ExportPackage() + protected override async Task ExportImpl() + { + return await this.Export(); + } + + private async new Task Export() { ASDebug.Log("Using native package exporter"); try { - var guids = GetGuids(_exportSettings.ExportPaths, out bool onlyFolders); + var guids = GetGuids(_legacyExportSettings.ExportPaths, out bool onlyFolders); if (guids.Length == 0 || onlyFolders) throw new ArgumentException("Package Exporting failed: provided export paths are empty or only contain empty folders"); string exportMethod = ExportMethodWithoutDependencies; - if (_exportSettings.IncludeDependencies) + if (_legacyExportSettings.IncludeDependencies) exportMethod = ExportMethodWithDependencies; var split = exportMethod.Split('.'); @@ -52,22 +59,22 @@ namespace AssetStoreTools.Exporter ASDebug.Log("Invoking native export method"); - method?.Invoke(null, new object[] { guids, _exportSettings.OutputFilename }); + method?.Invoke(null, new object[] { guids, _legacyExportSettings.OutputFilename }); // The internal exporter methods are asynchronous, therefore // we need to wait for exporting to finish before returning await Task.Run(() => { - while (!File.Exists(_exportSettings.OutputFilename)) + while (!File.Exists(_legacyExportSettings.OutputFilename)) Thread.Sleep(100); }); - ASDebug.Log($"Package file has been created at {_exportSettings.OutputFilename}"); - return new ExportResult() { Success = true, ExportedPath = _exportSettings.OutputFilename }; + ASDebug.Log($"Package file has been created at {_legacyExportSettings.OutputFilename}"); + return new PackageExporterResult() { Success = true, ExportedPath = _legacyExportSettings.OutputFilename }; } catch (Exception e) { - return new ExportResult() { Success = false, Error = ASError.GetGenericError(e) }; + return new PackageExporterResult() { Success = false, Exception = e }; } finally { diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs.meta similarity index 85% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs.meta index a345e4f..61f2057 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs.meta @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterLegacy.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/LegacyPackageExporter.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs new file mode 100644 index 0000000..a0ae7a5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs @@ -0,0 +1,13 @@ +using AssetStoreTools.Previews.Data; +using System; + +namespace AssetStoreTools.Exporter +{ + internal class PackageExporterResult + { + public bool Success; + public string ExportedPath; + public PreviewGenerationResult PreviewGenerationResult; + public Exception Exception; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs.meta similarity index 76% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs.meta index 2bc8809..549deb0 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ce99a618d1e211444b53f18bb3444f75 +guid: e685b1c322eab4540919d4fc970e812d MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/ExportResult.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporterResult.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs b/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs new file mode 100644 index 0000000..69e06da --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs @@ -0,0 +1,41 @@ +using AssetStoreTools.Previews.Data; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AssetStoreTools.Exporter +{ + internal class PreviewInjector : IPreviewInjector + { + private PreviewGenerationResult _result; + + public PreviewInjector(PreviewGenerationResult result) + { + _result = result; + } + + public void Inject(string temporaryPackagePath) + { + if (_result == null || !_result.Success) + return; + + var previews = _result.Previews.Where(x => x.Type == _result.GenerationType && x.Exists()); + InjectFilesIntoGuidFolders(previews, temporaryPackagePath); + } + + private void InjectFilesIntoGuidFolders(IEnumerable previews, string temporaryPackagePath) + { + foreach (var assetFolder in Directory.EnumerateDirectories(temporaryPackagePath)) + { + var guid = assetFolder.Replace("\\", "/").Split('/').Last(); + var generatedPreview = previews.FirstOrDefault(x => x.Guid.Equals(guid)); + + if (generatedPreview == null) + continue; + + // Note: Unity Importer and Asset Store only operate with .png extensions + File.Copy(generatedPreview.Path, $"{assetFolder}/preview.png", true); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs.meta similarity index 77% rename from Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs.meta index 934d0f0..1f7fe2e 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 52ef11a59e545544fafaa99a5fa6cce9 +guid: 772db784128e32d4792bb680258c71df MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/PackageExporter.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Exporter/PreviewInjector.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews.meta b/Packages/com.unity.asset-store-tools/Editor/Previews.meta new file mode 100644 index 0000000..747d5eb --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13e8cd63112e52d43a7e65949f0143a4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts.meta new file mode 100644 index 0000000..f95e49f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cbe1aebea6551424997b361fab69f266 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data.meta new file mode 100644 index 0000000..89f1356 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ae99e2e3b5a83d1469110306c96f4c58 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs new file mode 100644 index 0000000..2705875 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Data +{ + internal class CustomPreviewGenerationSettings : PreviewGenerationSettings + { + public override GenerationType GenerationType => GenerationType.Custom; + + public int Width; + public int Height; + public int Depth; + + public int NativeWidth; + public int NativeHeight; + + public Color AudioSampleColor; + public Color AudioBackgroundColor; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs.meta new file mode 100644 index 0000000..027edf9 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2ccb1292c1c4ba94cb6f4022ecfdfa50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/CustomPreviewGenerationSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs new file mode 100644 index 0000000..cb841a8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs @@ -0,0 +1,9 @@ +namespace AssetStoreTools.Previews.Data +{ + internal enum FileNameFormat + { + Guid = 0, + FullAssetPath = 1, + AssetName = 2, + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs.meta new file mode 100644 index 0000000..4806598 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 38a1babecfeaf524f98e8d67882acf48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/FileNameFormat.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs new file mode 100644 index 0000000..fef979f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs @@ -0,0 +1,9 @@ +namespace AssetStoreTools.Previews.Data +{ + internal enum GenerationType + { + Unknown = 0, + Native = 1, + Custom = 2 + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs.meta new file mode 100644 index 0000000..310ce82 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 66697a5d16404d948ba3191ddfc60bd9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/GenerationType.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs new file mode 100644 index 0000000..fff204e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs @@ -0,0 +1,10 @@ +namespace AssetStoreTools.Previews.Data +{ + internal class NativePreviewGenerationSettings : PreviewGenerationSettings + { + public override GenerationType GenerationType => GenerationType.Native; + public bool WaitForPreviews; + public bool ChunkedPreviewLoading; + public int ChunkSize; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs.meta new file mode 100644 index 0000000..a6119ea --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5b2ef019acae6fe43b5565858e15433a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/NativePreviewGenerationSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs new file mode 100644 index 0000000..cabc56c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.Data +{ + internal class PreviewDatabase + { + public List Previews; + + public PreviewDatabase() + { + Previews = new List(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs.meta new file mode 100644 index 0000000..4305540 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cf8cef28a68324742a7e4b47efc87563 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewDatabase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs new file mode 100644 index 0000000..e68f29a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Previews.Data +{ + internal enum PreviewFormat + { + JPG = 0, + PNG = 1 + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs.meta new file mode 100644 index 0000000..2ffac1d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0500e4459ebfe8448a13194af49f89fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewFormat.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs new file mode 100644 index 0000000..6fb7791 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.Data +{ + internal class PreviewGenerationResult + { + public GenerationType GenerationType; + public bool Success; + public IEnumerable GeneratedPreviews; + public IEnumerable Previews; + public Exception Exception; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs.meta new file mode 100644 index 0000000..22f1136 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e040f2cdf0177824dacb158b23a63374 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationResult.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs new file mode 100644 index 0000000..920ac13 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs @@ -0,0 +1,12 @@ +namespace AssetStoreTools.Previews.Data +{ + internal abstract class PreviewGenerationSettings + { + public abstract GenerationType GenerationType { get; } + public string[] InputPaths; + public string OutputPath; + public PreviewFormat Format; + public FileNameFormat PreviewFileNamingFormat; + public bool OverwriteExisting; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs.meta new file mode 100644 index 0000000..30ef418 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7e578ae6616505a4795da8f632d63229 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewGenerationSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs new file mode 100644 index 0000000..a7ba227 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace AssetStoreTools.Previews.Data +{ + internal class PreviewMetadata + { + public GenerationType Type; + public string Guid; + public string Name; + public string Path; + + public bool Exists() + { + return File.Exists(Path); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs.meta new file mode 100644 index 0000000..437956a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4ff6be4e277d8314e921baff52ea25bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Data/PreviewMetadata.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators.meta new file mode 100644 index 0000000..7019d5c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed651159a2004574789e97726da5090c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom.meta new file mode 100644 index 0000000..cac3d99 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f675855c3d971694785806c0c7a463be +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs new file mode 100644 index 0000000..f46e406 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom +{ + internal class AudioChannel + { + private int _yMin; + private int _yMax; + + private int _yBaseline; + private int _yAmplitude; + + private List _samples; + + public AudioChannel(int minY, int maxY, List samples) + { + _yMin = minY; + _yMax = maxY; + + _yBaseline = (_yMin + _yMax) / 2; + _yAmplitude = _yMax - _yBaseline; + + _samples = samples; + } + + public IEnumerable GetCoordinateData(int desiredWidth) + { + var coordinates = new List(); + var step = Mathf.RoundToInt((float)_samples.Count / desiredWidth); + + for (int i = 0; i < desiredWidth; i++) + { + var startIndex = i * step; + var endIndex = (i + 1) * step; + var sampleChunk = CreateChunk(startIndex, endIndex); + + if (sampleChunk.Count() == 0) + break; + + DownsampleMax(sampleChunk, out var aboveBaseline, out var belowBaseline); + + var yAboveBaseline = SampleToCoordinate(aboveBaseline); + var yBelowBaseline = SampleToCoordinate(belowBaseline); + + coordinates.Add(new AudioChannelCoordinate(i, _yBaseline, yAboveBaseline, yBelowBaseline)); + } + + // If there weren't enough samples to complete the desired width - fill out the rest with zeroes + for (int i = coordinates.Count; i < desiredWidth; i++) + coordinates.Add(new AudioChannelCoordinate(i, _yBaseline, 0, 0)); + + return coordinates; + } + + private IEnumerable CreateChunk(int startIndex, int endIndex) + { + var chunk = new List(); + for (int i = startIndex; i < endIndex; i++) + { + if (i >= _samples.Count) + break; + + chunk.Add(_samples[i]); + } + + return chunk; + } + + private void DownsampleMax(IEnumerable samples, out float valueAboveBaseline, out float valueBelowBaseline) + { + valueAboveBaseline = 0; + valueBelowBaseline = 0; + + foreach (var sample in samples) + { + if (sample > 0 && sample > valueAboveBaseline) + { + valueAboveBaseline = sample; + continue; + } + + if (sample < 0 && sample < valueBelowBaseline) + { + valueBelowBaseline = sample; + continue; + } + } + } + + private int SampleToCoordinate(float sample) + { + return _yBaseline + (int)(sample * _yAmplitude); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs.meta new file mode 100644 index 0000000..dbbceb3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 82fab55b08a1be94cb2e18f3feae91ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs new file mode 100644 index 0000000..1030eed --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs @@ -0,0 +1,18 @@ +namespace AssetStoreTools.Previews.Generators.Custom +{ + internal struct AudioChannelCoordinate + { + public int X { get; private set; } + public int YBaseline { get; private set; } + public int YAboveBaseline { get; private set; } + public int YBelowBaseline { get; private set; } + + public AudioChannelCoordinate(int x, int yBaseline, int yAboveBaseline, int yBelowBaseline) + { + X = x; + YBaseline = yBaseline; + YAboveBaseline = yAboveBaseline; + YBelowBaseline = yBelowBaseline; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs.meta new file mode 100644 index 0000000..5c41aae --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b54462f6af82a2644944d6e4bde23c9e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters.meta new file mode 100644 index 0000000..91cfc9f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c07070deed666d54cb72a89a5fcd7ef7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs new file mode 100644 index 0000000..ec7cd57 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal interface ISceneScreenshotter + { + SceneScreenshotterSettings Settings { get; } + + string Screenshot(string outputPath); + string Screenshot(GameObject target, string outputPath); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/AssetValidationElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs.meta similarity index 58% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/AssetValidationElement.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs.meta index 0362138..103f3a0 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/AssetValidationElement.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 118376dcb318c4341b1df6773e3d8d4c +guid: 045ac265a792af243918af0849ee2ac8 MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/AssetValidationElement.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs new file mode 100644 index 0000000..a446208 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs @@ -0,0 +1,32 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal class MaterialScreenshotter : SceneScreenshotterBase + { + public MaterialScreenshotter(SceneScreenshotterSettings settings) : base(settings) { } + + public override void PositionCamera(GameObject target) + { + var renderers = target.GetComponentsInChildren(); + if (renderers == null || renderers.Length == 0) + return; + + var bounds = GetGlobalBounds(renderers); + + var materialSphereRadius = bounds.extents.y * 1.1f; + + var angle = Camera.fieldOfView / 2; + var sinAngle = Mathf.Sin(angle * Mathf.Deg2Rad); + var distance = materialSphereRadius / sinAngle; + + Camera.transform.position = new Vector3(bounds.center.x, bounds.center.y + distance, bounds.center.z); + Camera.transform.LookAt(bounds.center); + Camera.transform.RotateAround(bounds.center, Vector3.left, 60); + Camera.transform.RotateAround(bounds.center, Vector3.up, -45); + + Camera.nearClipPlane = 0.01f; + Camera.farClipPlane = 10000; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/PackageValidationElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs.meta similarity index 59% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/PackageValidationElement.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs.meta index 11fe3be..fc8ef16 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/PackageValidationElement.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 33a72e7596565c749a495b4213579a67 +guid: c2bd7b01b0cebeb43a6fbc53377f0ea6 MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Upload/Validation/PackageValidationElement.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs new file mode 100644 index 0000000..6ce5905 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal class MeshScreenshotter : SceneScreenshotterBase + { + public MeshScreenshotter(SceneScreenshotterSettings settings) : base(settings) { } + + public override void PositionCamera(GameObject target) + { + var renderers = target.GetComponentsInChildren(); + if (renderers == null || renderers.Length == 0) + return; + + var bounds = GetGlobalBounds(renderers); + + var encapsulatingSphereDiameter = (bounds.max - bounds.min).magnitude; + var encapsulatingSphereRadius = encapsulatingSphereDiameter / 2; + + var angle = Camera.fieldOfView / 2; + var sinAngle = Mathf.Sin(angle * Mathf.Deg2Rad); + var distance = encapsulatingSphereRadius / sinAngle; + + Camera.transform.position = new Vector3(bounds.center.x, bounds.center.y + distance, bounds.center.z); + Camera.transform.LookAt(bounds.center); + Camera.transform.RotateAround(bounds.center, Vector3.left, 65); + Camera.transform.RotateAround(bounds.center, Vector3.up, 235); + + Camera.nearClipPlane = 0.01f; + Camera.farClipPlane = 10000; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs.meta new file mode 100644 index 0000000..7802999 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f0339d22d91b7c94ebc18b1de6f1e287 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs new file mode 100644 index 0000000..fb289f4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs @@ -0,0 +1,124 @@ +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal abstract class SceneScreenshotterBase : ISceneScreenshotter + { + public SceneScreenshotterSettings Settings { get; } + + protected Camera Camera => GetCamera(); + private Camera _camera; + + public SceneScreenshotterBase(SceneScreenshotterSettings settings) + { + Settings = settings; + } + + private Camera GetCamera() + { + if (_camera == null) + { +#if UNITY_2022_3_OR_NEWER + _camera = GameObject.FindFirstObjectByType(FindObjectsInactive.Include); +#else + _camera = GameObject.FindObjectOfType(); +#endif + } + + return _camera; + } + + public virtual void ValidateSettings() + { + if (Settings.Width <= 0) + throw new ArgumentException("Width should be larger than 0"); + + if (Settings.Height <= 0) + throw new ArgumentException("Height should be larger than 0"); + + if (Settings.Depth <= 0) + throw new ArgumentException("Depth should be larger than 0"); + + if (Settings.NativeWidth <= 0) + throw new ArgumentException("Native width should be larger than 0"); + + if (Settings.NativeHeight <= 0) + throw new ArgumentException("Native height should be larger than 0"); + } + + public abstract void PositionCamera(GameObject target); + + public string Screenshot(string outputPath) + { + ValidateSettings(); + + var texture = GraphicsUtility.GetTextureFromCamera(Camera, Settings.NativeWidth, Settings.NativeHeight, Settings.Depth); + + if (Settings.Width < Settings.NativeWidth || Settings.Height < Settings.NativeHeight) + texture = GraphicsUtility.ResizeTexture(texture, Settings.Width, Settings.Height); + + var extension = PreviewConvertUtility.ConvertExtension(Settings.Format); + var writtenPath = $"{outputPath}.{extension}"; + var bytes = PreviewConvertUtility.ConvertTexture(texture, Settings.Format); + File.WriteAllBytes(writtenPath, bytes); + + return writtenPath; + } + + public string Screenshot(GameObject target, string outputPath) + { + PositionCamera(target); + PositionLighting(target); + return Screenshot(outputPath); + } + + private void PositionLighting(GameObject target) + { +#if UNITY_2022_3_OR_NEWER + var light = GameObject.FindFirstObjectByType(FindObjectsInactive.Include); +#else + var light = GameObject.FindObjectOfType(); +#endif + light.transform.position = Camera.transform.position; + light.transform.LookAt(target.transform); + light.transform.RotateAround(target.transform.position, Vector3.forward, 60f); + } + + protected Bounds GetGlobalBounds(IEnumerable renderers) + { + var center = Vector3.zero; + + foreach (var renderer in renderers) + { + center += renderer.bounds.center; + } + center /= renderers.Count(); + + var globalBounds = new Bounds(center, Vector3.zero); + + foreach (var renderer in renderers) + { + globalBounds.Encapsulate(renderer.bounds); + } + + return globalBounds; + } + + protected Bounds GetNormalizedBounds(Bounds bounds) + { + var largestExtent = Mathf.Max(bounds.extents.x, bounds.extents.y, bounds.extents.z); + var normalizedBounds = new Bounds() + { + center = bounds.center, + extents = new Vector3(largestExtent, largestExtent, largestExtent) + }; + + return normalizedBounds; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs.meta new file mode 100644 index 0000000..bf6bbf0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ce77aefdce8a37f498d17d73da53d0a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs new file mode 100644 index 0000000..82e0ec1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Previews.Data; + +namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters +{ + internal class SceneScreenshotterSettings + { + public int Width; + public int Height; + public int Depth; + + public int NativeWidth; + public int NativeHeight; + + public PreviewFormat Format; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs.meta new file mode 100644 index 0000000..04d76d7 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aa34806e243bad949892d06dd47295e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators.meta new file mode 100644 index 0000000..e13d726 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4bb2dd0960418d4a8d4efd34b92a418 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs new file mode 100644 index 0000000..36a6b21 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Previews.Data; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class AudioTypeGeneratorSettings : TypeGeneratorSettings + { + public int Width; + public int Height; + + public Color SampleColor; + public Color BackgroundColor; + public PreviewFormat Format; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs.meta new file mode 100644 index 0000000..fbf89c9 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5b7ab1b072d95be4daf221ee23af1c80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs new file mode 100644 index 0000000..a0f1545 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs @@ -0,0 +1,207 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class AudioTypePreviewGenerator : TypePreviewGeneratorBase + { + private AudioTypeGeneratorSettings _settings; + private Texture2D _texture; + + public override event Action OnAssetProcessed; + + public AudioTypePreviewGenerator(AudioTypeGeneratorSettings settings) : base(settings) + { + _settings = settings; + } + + public override void ValidateSettings() + { + base.ValidateSettings(); + + if (_settings.Width <= 0) + throw new ArgumentException("Width must be larger than 0"); + + if (_settings.Height <= 0) + throw new ArgumentException("Height must be larger than 0"); + } + + protected override IEnumerable CollectAssets() + { + var assets = new List(); + var materialGuids = AssetDatabase.FindAssets("t:audioclip", Settings.InputPaths); + foreach (var guid in materialGuids) + { + var audioClip = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested audio clips + if (!AssetDatabase.IsMainAsset(audioClip)) + continue; + + // Skip materials with an error shader + if (!IsLoadTypeSupported(audioClip)) + { + Debug.LogWarning($"Audio clip '{audioClip}' is using a load type which cannot retrieve sample data. Preview will not be generated."); + continue; + } + + assets.Add(audioClip); + } + + return assets; + } + + private bool IsLoadTypeSupported(AudioClip audioClip) + { + if (audioClip.loadType == AudioClipLoadType.DecompressOnLoad) + return true; + + return false; + } + + protected override async Task> GenerateImpl(IEnumerable assets) + { + var generatedPreviews = new List(); + var audioClips = assets.ToList(); + for (int i = 0; i < audioClips.Count; i++) + { + var audioClip = audioClips[i] as AudioClip; + if (audioClip != null) + { + var texture = GenerateAudioClipTexture(audioClip); + + var outputPath = GenerateOutputPathWithExtension(audioClip, _settings.PreviewFileNamingFormat, _settings.Format); + var bytes = PreviewConvertUtility.ConvertTexture(texture, _settings.Format); + File.WriteAllBytes(outputPath, bytes); + generatedPreviews.Add(ObjectToMetadata(audioClip, outputPath)); + } + + OnAssetProcessed?.Invoke(i, audioClips.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private Texture2D GenerateAudioClipTexture(AudioClip audioClip) + { + if (!audioClip.LoadAudioData()) + throw new Exception("Could not load audio data"); + + try + { + if (_texture == null) + _texture = new Texture2D(_settings.Width, _settings.Height); + else +#if UNITY_2021_3_OR_NEWER || UNITY_2022_1_OR_NEWER || UNITY_2021_2_OR_NEWER + _texture.Reinitialize(_settings.Width, _settings.Height); +#else + _texture.Resize(_settings.Width, _settings.Height); +#endif + + FillTextureBackground(); + FillTextureForeground(audioClip); + + _texture.Apply(); + return _texture; + } + finally + { + audioClip.UnloadAudioData(); + } + } + + private void FillTextureBackground() + { + for (int i = 0; i < _texture.width; i++) + { + for (int j = 0; j < _texture.height; j++) + { + _texture.SetPixel(i, j, _settings.BackgroundColor); + } + } + } + + private void FillTextureForeground(AudioClip audioClip) + { + var channels = CreateChannels(audioClip); + + for (int i = 0; i < channels.Count; i++) + { + DrawChannel(channels[i]); + } + } + + private List CreateChannels(AudioClip audioClip) + { + var channelSamples = GetChannelSamples(audioClip); + var sectionSize = _texture.height / audioClip.channels; + + var channels = new List(); + + for (int i = 0; i < audioClip.channels; i++) + { + var channelMaxY = (_texture.height - 1) - i * sectionSize; + var channelMinY = _texture.height - (i + 1) * sectionSize; + var channel = new AudioChannel(channelMinY, channelMaxY, channelSamples[i]); + channels.Add(channel); + } + + return channels; + } + + private List> GetChannelSamples(AudioClip audioClip) + { + var channelSamples = new List>(); + var allSamples = new float[audioClip.samples * audioClip.channels]; + + if (!audioClip.GetData(allSamples, 0)) + throw new Exception("Could not retrieve audio samples"); + + for (int i = 0; i < audioClip.channels; i++) + { + var samples = new List(); + var sampleIndex = i; + while (sampleIndex < allSamples.Length) + { + samples.Add(allSamples[sampleIndex]); + sampleIndex += audioClip.channels; + } + + channelSamples.Add(samples); + } + + return channelSamples; + } + + private void DrawChannel(AudioChannel channel) + { + var sectionData = channel.GetCoordinateData(_texture.width); + + foreach (var data in sectionData) + { + DrawVerticalColumn(data.X, data.YBaseline, data.YAboveBaseline, data.YBelowBaseline, _settings.SampleColor); + } + } + + private void DrawVerticalColumn(int x, int yBaseline, int y1, int y2, Color color) + { + _texture.SetPixel(x, yBaseline, color); + + var startIndex = y1 < y2 ? y1 : y2; + var endIndex = y1 < y2 ? y2 : y1; + + for (int i = startIndex; i < endIndex; i++) + { + _texture.SetPixel(x, i, color); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..11499cf --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9ddf69bb2dca51a42aff247b3a471bb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs new file mode 100644 index 0000000..bdd5056 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal interface ITypePreviewGenerator + { + TypeGeneratorSettings Settings { get; } + + event Action OnAssetProcessed; + + Task> Generate(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs.meta new file mode 100644 index 0000000..8069f54 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d9cd368dc73a23478390ee1332cb0be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs new file mode 100644 index 0000000..13b44ec --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs @@ -0,0 +1,85 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class MaterialTypePreviewGenerator : TypePreviewGeneratorFromScene + { + public override event Action OnAssetProcessed; + + public MaterialTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { } + + protected override IEnumerable CollectAssets() + { + var assets = new List(); + var materialGuids = AssetDatabase.FindAssets("t:material", Settings.InputPaths); + foreach (var guid in materialGuids) + { + var mat = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested materials + if (!AssetDatabase.IsMainAsset(mat)) + continue; + + // Skip materials with an error shader + if (IsShaderInvalid(mat.shader)) + { + Debug.LogWarning($"Material '{mat}' is using an erroring shader. Preview will not be generated."); + continue; + } + + assets.Add(mat); + } + + return assets; + } + + protected override async Task> GeneratePreviewsInScene(IEnumerable assets) + { + var generatedPreviews = new List(); + var materials = assets.ToList(); + var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); + + var hasMeshRenderer = sphere.TryGetComponent(out var meshRenderer); + if (!hasMeshRenderer) + throw new Exception($"Could not find a MeshRenderer for {sphere}"); + + for (int i = 0; i < materials.Count; i++) + { + ThrowIfSceneChanged(); + + var material = materials[i] as Material; + + if (material != null) + { + meshRenderer.sharedMaterial = material; + var previewPath = Settings.Screenshotter.Screenshot(sphere, GenerateOutputPathWithoutExtension(material, Settings.PreviewFileNamingFormat)); + if (!string.IsNullOrEmpty(previewPath)) + generatedPreviews.Add(ObjectToMetadata(material, previewPath)); + } + + OnAssetProcessed?.Invoke(i, materials.Count); + await Task.Yield(); + } + + UnityEngine.Object.DestroyImmediate(sphere); + return generatedPreviews; + } + + private bool IsShaderInvalid(Shader shader) + { + if (ShaderUtil.ShaderHasError(shader)) + return true; + + if (!shader.isSupported) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..b4edb16 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a4e121bae63a199458e53a523dd18c8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs new file mode 100644 index 0000000..7d97607 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs @@ -0,0 +1,86 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class ModelTypePreviewGenerator : TypePreviewGeneratorFromScene + { + public override event Action OnAssetProcessed; + + public ModelTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { } + + protected override IEnumerable CollectAssets() + { + var models = new List(); + var modelGuids = AssetDatabase.FindAssets("t:model", Settings.InputPaths); + + foreach (var guid in modelGuids) + { + var model = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested models + if (!AssetDatabase.IsMainAsset(model)) + continue; + + // Skip models without renderers + if (model.GetComponentsInChildren().Length == 0) + continue; + + models.Add(model); + } + + return models; + } + + protected override async Task> GeneratePreviewsInScene(IEnumerable assets) + { + var generatedPreviews = new List(); + var models = assets.ToList(); + var referenceShader = GetDefaultObjectShader(); + + for (int i = 0; i < models.Count; i++) + { + ThrowIfSceneChanged(); + + var model = models[i] as GameObject; + + if (model != null) + { + var go = UnityEngine.Object.Instantiate(model, Vector3.zero, Quaternion.Euler(0, 0, 0)); + ReplaceShaders(go, referenceShader); + + var previewPath = Settings.Screenshotter.Screenshot(go, GenerateOutputPathWithoutExtension(model, Settings.PreviewFileNamingFormat)); + if (!string.IsNullOrEmpty(previewPath)) + generatedPreviews.Add(ObjectToMetadata(model, previewPath)); + + UnityEngine.Object.DestroyImmediate(go); + } + + OnAssetProcessed?.Invoke(i, models.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private void ReplaceShaders(GameObject go, Shader shader) + { + var meshRenderers = go.GetComponentsInChildren(); + foreach (var mr in meshRenderers) + { + var materialArray = mr.sharedMaterials; + for (int i = 0; i < materialArray.Length; i++) + { + materialArray[i] = new Material(shader) { color = new Color(0.7f, 0.7f, 0.7f) }; + } + + mr.sharedMaterials = materialArray; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..dacc7e3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fca8a1fa8a211874cb84d3d811a0158c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs new file mode 100644 index 0000000..f0eeb6b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs @@ -0,0 +1,113 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class PrefabTypePreviewGenerator : TypePreviewGeneratorFromScene + { + public override event Action OnAssetProcessed; + + public PrefabTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { } + + protected override IEnumerable CollectAssets() + { + var prefabs = new List(); + var prefabGuids = AssetDatabase.FindAssets("t:prefab", Settings.InputPaths); + + foreach (var guid in prefabGuids) + { + var prefab = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested prefabs + if (!AssetDatabase.IsMainAsset(prefab)) + continue; + + // Skip prefabs without renderers + if (prefab.GetComponentsInChildren().Length == 0) + continue; + + prefabs.Add(prefab); + } + + return prefabs; + } + + protected override async Task> GeneratePreviewsInScene(IEnumerable assets) + { + var generatedPreviews = new List(); + var prefabs = assets.ToList(); + var objectReferenceShader = GetDefaultObjectShader(); + var particleReferenceShader = GetDefaultParticleShader(); + + for (int i = 0; i < prefabs.Count; i++) + { + ThrowIfSceneChanged(); + + var prefab = prefabs[i] as GameObject; + if (prefab != null) + { + var go = UnityEngine.Object.Instantiate(prefab, Vector3.zero, Quaternion.Euler(0, 0, 0)); + + ReplaceMissingShaders(go, objectReferenceShader, particleReferenceShader); + + HandleParticleSystems(go); + + var previewPath = Settings.Screenshotter.Screenshot(go, GenerateOutputPathWithoutExtension(prefab, Settings.PreviewFileNamingFormat)); + if (!string.IsNullOrEmpty(previewPath)) + generatedPreviews.Add(ObjectToMetadata(prefab, previewPath)); + + UnityEngine.Object.DestroyImmediate(go); + } + + OnAssetProcessed?.Invoke(i, prefabs.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private void ReplaceMissingShaders(GameObject go, Shader objectShader, Shader particleShader) + { + var meshRenderers = go.GetComponentsInChildren(); + foreach (var mr in meshRenderers) + { + var shaderToUse = mr is ParticleSystemRenderer ? particleShader : objectShader; + + var materialArray = mr.sharedMaterials; + for (int i = 0; i < materialArray.Length; i++) + { + if (materialArray[i] == null) + { + materialArray[i] = new Material(shaderToUse); + } + else if (!materialArray[i].shader.isSupported) + { + materialArray[i].shader = shaderToUse; + } + } + + mr.sharedMaterials = materialArray; + } + } + + private void HandleParticleSystems(GameObject go) + { + var particleSystems = go.GetComponentsInChildren(); + if (particleSystems.Length == 0) + return; + + foreach (var ps in particleSystems) + { + ps.Stop(); + ps.Clear(); + ps.randomSeed = 1; + ps.Simulate(10, false, true, false); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..ce81404 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 24b15b10bc361c84581f46cb6dd644cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs new file mode 100644 index 0000000..666b32d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Previews.Data; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class TextureTypeGeneratorSettings : TypeGeneratorSettings + { + public int MaxWidth; + public int MaxHeight; + public PreviewFormat Format; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs.meta new file mode 100644 index 0000000..09acc0d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 058d746982619b04eb5e200363003899 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs new file mode 100644 index 0000000..24e1102 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs @@ -0,0 +1,116 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class TextureTypePreviewGenerator : TypePreviewGeneratorBase + { + private TextureTypeGeneratorSettings _settings; + + public override event Action OnAssetProcessed; + + public TextureTypePreviewGenerator(TextureTypeGeneratorSettings settings) : base(settings) + { + _settings = settings; + } + + public override void ValidateSettings() + { + base.ValidateSettings(); + + if (_settings.MaxWidth <= 0) + throw new ArgumentException("Max width should be larger than 0"); + + if (_settings.MaxHeight <= 0) + throw new ArgumentException("Max height should be larger than 0"); + } + + protected override IEnumerable CollectAssets() + { + var textures = new List(); + var textureGuids = AssetDatabase.FindAssets("t:texture", Settings.InputPaths); + + foreach (var guid in textureGuids) + { + var texture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); + + // Skip nested textures + if (!AssetDatabase.IsMainAsset(texture)) + continue; + + textures.Add(texture); + } + + return textures; + } + + protected override async Task> GenerateImpl(IEnumerable assets) + { + var generatedPreviews = new List(); + var textures = assets.ToList(); + + for (int i = 0; i < textures.Count; i++) + { + var texture = textures[i] as Texture2D; + + if (texture != null) + { + Texture2D resizedTexture; + CalculateTextureSize(texture, out var resizeWidth, out var resizeHeight); + + var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter; + if (importer != null && importer.textureType == TextureImporterType.NormalMap) + resizedTexture = GraphicsUtility.ResizeTextureNormalMap(texture, resizeWidth, resizeHeight); + else + resizedTexture = GraphicsUtility.ResizeTexture(texture, resizeWidth, resizeHeight); + + var previewPath = GenerateOutputPathWithExtension(texture, _settings.PreviewFileNamingFormat, _settings.Format); + + // Some textures may be transparent and need to be encoded as PNG to look correctly + var targetFormat = texture.alphaIsTransparency ? PreviewFormat.PNG : _settings.Format; + var bytes = PreviewConvertUtility.ConvertTexture(resizedTexture, targetFormat); + + File.WriteAllBytes(previewPath, bytes); + generatedPreviews.Add(ObjectToMetadata(texture, previewPath)); + } + + OnAssetProcessed?.Invoke(i, textures.Count); + await Task.Yield(); + } + + return generatedPreviews; + } + + private void CalculateTextureSize(Texture2D texture, out int width, out int height) + { + if (texture.width <= _settings.MaxWidth && texture.height <= _settings.MaxHeight) + { + width = texture.width; + height = texture.height; + return; + } + + var widthLongerThanHeight = texture.width > texture.height; + + if (widthLongerThanHeight) + { + var ratio = (float)texture.width / texture.height; + width = _settings.MaxWidth; + height = Mathf.RoundToInt(width / ratio); + } + else + { + var ratio = (float)texture.height / texture.width; + height = _settings.MaxHeight; + width = Mathf.RoundToInt(height / ratio); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs.meta new file mode 100644 index 0000000..90d58b7 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b04f55867ee575c489803356220feb31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs new file mode 100644 index 0000000..1aaaa42 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs @@ -0,0 +1,12 @@ +using AssetStoreTools.Previews.Data; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal abstract class TypeGeneratorSettings + { + public string[] InputPaths; + public string[] IgnoredGuids; + public string OutputPath; + public FileNameFormat PreviewFileNamingFormat; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs.meta new file mode 100644 index 0000000..90f895d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a224a4b41a8c7cf4cb53dd77d6f2518b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs new file mode 100644 index 0000000..a49d968 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs @@ -0,0 +1,126 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal abstract class TypePreviewGeneratorBase : ITypePreviewGenerator + { + public TypeGeneratorSettings Settings { get; } + + public abstract event Action OnAssetProcessed; + + public TypePreviewGeneratorBase(TypeGeneratorSettings settings) + { + Settings = settings; + } + + public virtual void ValidateSettings() + { + if (Settings.InputPaths == null || Settings.InputPaths.Length == 0) + throw new ArgumentException("Input path cannot be null"); + + foreach (var path in Settings.InputPaths) + { + var inputPath = path.EndsWith("/") ? path.Remove(path.Length - 1) : path; + if (!AssetDatabase.IsValidFolder(inputPath)) + throw new ArgumentException($"Input path '{inputPath}' is not a valid ADB folder"); + } + + if (string.IsNullOrEmpty(Settings.OutputPath)) + throw new ArgumentException("Output path cannot be null"); + } + + public async Task> Generate() + { + var generatedPreviews = new List(); + ValidateSettings(); + + var assets = CollectAssets(); + assets = FilterIgnoredAssets(assets); + + if (assets.Count() == 0) + return generatedPreviews; + + return await GenerateImpl(assets); + } + + protected abstract IEnumerable CollectAssets(); + + private IEnumerable FilterIgnoredAssets(IEnumerable assets) + { + if (Settings.IgnoredGuids == null || Settings.IgnoredGuids.Length == 0) + return assets; + + var filteredAssets = new List(); + foreach (var asset in assets) + { + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long _)) + continue; + + if (Settings.IgnoredGuids.Any(x => x == guid)) + continue; + + filteredAssets.Add(asset); + } + + return filteredAssets; + } + + protected abstract Task> GenerateImpl(IEnumerable assets); + + protected PreviewMetadata ObjectToMetadata(UnityEngine.Object obj, string previewPath) + { + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _)) + throw new Exception($"Could not retrieve guid for object {obj}"); + + return new PreviewMetadata() + { + Type = GenerationType.Custom, + Guid = guid, + Name = obj.name, + Path = previewPath + }; + } + + protected string GenerateOutputPathWithoutExtension(UnityEngine.Object asset, FileNameFormat fileNameFormat) + { + PrepareOutputFolder(Settings.OutputPath, false); + var directoryPath = Settings.OutputPath; + var fileName = PreviewConvertUtility.ConvertFilename(asset, fileNameFormat); + var fullPath = $"{directoryPath}/{fileName}"; + + return fullPath; + } + + protected string GenerateOutputPathWithExtension(UnityEngine.Object asset, FileNameFormat fileNameFormat, PreviewFormat previewFormat) + { + var partialOutputPath = GenerateOutputPathWithoutExtension(asset, fileNameFormat); + var extension = PreviewConvertUtility.ConvertExtension(previewFormat); + + return $"{partialOutputPath}.{extension}"; + } + + private void PrepareOutputFolder(string outputPath, bool cleanup) + { + var dir = new DirectoryInfo(outputPath); + + if (!dir.Exists) + { + dir.Create(); + return; + } + + if (!cleanup) + return; + + dir.Delete(true); + dir.Create(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs.meta new file mode 100644 index 0000000..8b3faa8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c6fb2d639232bce4698338a252f47f3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs new file mode 100644 index 0000000..f9fe453 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs @@ -0,0 +1,111 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal abstract class TypePreviewGeneratorFromScene : TypePreviewGeneratorBase + { + protected new TypePreviewGeneratorFromSceneSettings Settings; + + private CancellationTokenSource _cancellationTokenSource; + + public TypePreviewGeneratorFromScene(TypePreviewGeneratorFromSceneSettings settings) : base(settings) + { + Settings = settings; + } + + public override void ValidateSettings() + { + base.ValidateSettings(); + + if (Settings.Screenshotter == null) + throw new ArgumentException("Screenshotter cannot be null"); + } + + protected sealed override async Task> GenerateImpl(IEnumerable assets) + { + var originalScenePath = EditorSceneManager.GetActiveScene().path; + await PreviewSceneUtility.OpenPreviewSceneForCurrentPipeline(); + + try + { + _cancellationTokenSource = new CancellationTokenSource(); + EditorSceneManager.sceneOpened += SceneOpenedDuringGeneration; + return await GeneratePreviewsInScene(assets); + } + finally + { + EditorSceneManager.sceneOpened -= SceneOpenedDuringGeneration; + _cancellationTokenSource.Dispose(); + if (!string.IsNullOrEmpty(originalScenePath)) + EditorSceneManager.OpenScene(originalScenePath); + } + } + + protected abstract Task> GeneratePreviewsInScene(IEnumerable assets); + + private void SceneOpenedDuringGeneration(Scene _, OpenSceneMode __) + { + if (!_cancellationTokenSource.IsCancellationRequested) + _cancellationTokenSource.Cancel(); + } + + protected void ThrowIfSceneChanged() + { + if (_cancellationTokenSource.Token.IsCancellationRequested) + throw new Exception("Preview generation was aborted due to a change of the scene"); + } + + protected Shader GetDefaultObjectShader() + { + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + return Shader.Find("Standard"); + case RenderPipeline.URP: + return Shader.Find("Universal Render Pipeline/Lit"); + case RenderPipeline.HDRP: + return Shader.Find("HDRP/Lit"); + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + + protected Shader GetDefaultParticleShader() + { + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + return Shader.Find("Particles/Standard Unlit"); + case RenderPipeline.URP: + return Shader.Find("Universal Render Pipeline/Particles/Unlit"); + case RenderPipeline.HDRP: + return Shader.Find("HDRP/Unlit"); + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + + protected Shader GetDefaultTextureShader() + { + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + return Shader.Find("Unlit/Texture"); + case RenderPipeline.URP: + return Shader.Find("Universal Render Pipeline/Unlit"); + case RenderPipeline.HDRP: + return Shader.Find("HDRP/Unlit"); + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs.meta new file mode 100644 index 0000000..263c544 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1bf0eb8d7ef65f340be785dae96e4b73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs new file mode 100644 index 0000000..36a2e6e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs @@ -0,0 +1,9 @@ +using AssetStoreTools.Previews.Generators.Custom.Screenshotters; + +namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators +{ + internal class TypePreviewGeneratorFromSceneSettings : TypeGeneratorSettings + { + public ISceneScreenshotter Screenshotter; + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs.meta new file mode 100644 index 0000000..d51befd --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a6b871798f99ad44d9fca46789239ec1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs new file mode 100644 index 0000000..7a1ffeb --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs @@ -0,0 +1,213 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using AssetStoreTools.Previews.Generators.Custom.Screenshotters; +using AssetStoreTools.Previews.Generators.Custom.TypeGenerators; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Previews +{ + internal class CustomPreviewGenerator : PreviewGeneratorBase + { + private CustomPreviewGenerationSettings _customSettings; + + public override event Action OnProgressChanged; + + public CustomPreviewGenerator(CustomPreviewGenerationSettings settings) + : base(settings) + { + _customSettings = settings; + } + + protected override void Validate() + { + base.Validate(); + + if (_customSettings.Width <= 0) + throw new ArgumentException("Width should be larger than 0"); + + if (_customSettings.Height <= 0) + throw new ArgumentException("Height should be larger than 0"); + + if (_customSettings.Depth <= 0) + throw new ArgumentException("Depth should be larger than 0"); + + if (_customSettings.NativeWidth <= 0) + throw new ArgumentException("Native width should be larger than 0"); + + if (_customSettings.NativeHeight <= 0) + throw new ArgumentException("Native height should be larger than 0"); + } + + protected override async Task GenerateImpl() + { + var result = new PreviewGenerationResult() + { + GenerationType = _customSettings.GenerationType + }; + + OnProgressChanged?.Invoke(0f); + + var generatedPreviews = new List(); + var existingPreviews = GetExistingPreviews(); + var generators = CreateGenerators(existingPreviews); + + var currentGenerator = 0; + Action generatorProgressCallback = null; + generatorProgressCallback = (currentAsset, totalAssets) => ReportProgress(currentGenerator, generators.Count(), currentAsset, totalAssets); + + try + { + foreach (var generator in generators) + { + generator.OnAssetProcessed += generatorProgressCallback; + var typeGeneratorPreviews = await generator.Generate(); + generatedPreviews.AddRange(typeGeneratorPreviews); + currentGenerator++; + } + + AssetDatabase.Refresh(); + + var allPreviews = new List(); + allPreviews.AddRange(generatedPreviews); + allPreviews.AddRange(existingPreviews); + + result.Success = true; + result.GeneratedPreviews = generatedPreviews; + result.Previews = allPreviews; + } + catch (Exception e) + { + result.Success = false; + result.Exception = e; + } + finally + { + foreach (var generator in generators) + generator.OnAssetProcessed -= generatorProgressCallback; + } + + return result; + } + + private IEnumerable GetExistingPreviews() + { + var existingPreviews = new List(); + + if (Settings.OverwriteExisting || !CachingService.GetCachedMetadata(out var database)) + return existingPreviews; + + var inputGuids = AssetDatabase.FindAssets("", _customSettings.InputPaths); + existingPreviews = database.Previews.Where(x => x.Type == GenerationType.Custom && x.Exists() && inputGuids.Any(y => y.Equals(x.Guid))).ToList(); + return existingPreviews; + } + + private IEnumerable CreateGenerators(IEnumerable existingPreviews) + { + var ignoredGuids = existingPreviews.Select(x => x.Guid).ToArray(); + + var generators = new ITypePreviewGenerator[] + { + CreateAudioPreviewGenerator(ignoredGuids), + CreateMaterialPreviewGenerator(ignoredGuids), + CreateModelPreviewGenerator(ignoredGuids), + CreatePrefabPreviewGenerator(ignoredGuids), + CreateTexturePreviewGenerator(ignoredGuids) + }; + + return generators; + } + + private ITypePreviewGenerator CreateAudioPreviewGenerator(string[] ignoredGuids) + { + var settings = new AudioTypeGeneratorSettings() + { + Width = _customSettings.Width, + Height = _customSettings.Height, + InputPaths = _customSettings.InputPaths, + OutputPath = _customSettings.OutputPath, + PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat, + Format = _customSettings.Format, + SampleColor = _customSettings.AudioSampleColor, + BackgroundColor = _customSettings.AudioBackgroundColor, + IgnoredGuids = ignoredGuids + }; + + return new AudioTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreateMaterialPreviewGenerator(string[] ignoredGuids) + { + var settings = CreateSceneGeneratorSettings(new MaterialScreenshotter(CreateScreenshotterSettings()), ignoredGuids); + return new MaterialTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreateModelPreviewGenerator(string[] ignoredGuids) + { + var settings = CreateSceneGeneratorSettings(new MeshScreenshotter(CreateScreenshotterSettings()), ignoredGuids); + return new ModelTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreatePrefabPreviewGenerator(string[] ignoredGuids) + { + var settings = CreateSceneGeneratorSettings(new MeshScreenshotter(CreateScreenshotterSettings()), ignoredGuids); + return new PrefabTypePreviewGenerator(settings); + } + + private ITypePreviewGenerator CreateTexturePreviewGenerator(string[] ignoredGuids) + { + var settings = new TextureTypeGeneratorSettings() + { + MaxWidth = _customSettings.Width, + MaxHeight = _customSettings.Height, + InputPaths = _customSettings.InputPaths, + OutputPath = _customSettings.OutputPath, + Format = _customSettings.Format, + PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat, + IgnoredGuids = ignoredGuids + }; + + return new TextureTypePreviewGenerator(settings); + } + + private TypePreviewGeneratorFromSceneSettings CreateSceneGeneratorSettings(ISceneScreenshotter screenshotter, string[] ignoredGuids) + { + var settings = new TypePreviewGeneratorFromSceneSettings() + { + InputPaths = _customSettings.InputPaths, + OutputPath = _customSettings.OutputPath, + PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat, + Screenshotter = screenshotter, + IgnoredGuids = ignoredGuids + }; + + return settings; + } + + private SceneScreenshotterSettings CreateScreenshotterSettings() + { + var settings = new SceneScreenshotterSettings() + { + Width = _customSettings.Width, + Height = _customSettings.Height, + Depth = _customSettings.Depth, + Format = _customSettings.Format, + NativeWidth = _customSettings.NativeWidth, + NativeHeight = _customSettings.NativeHeight, + }; + + return settings; + } + + private void ReportProgress(int currentGenerator, int totalGenerators, int currentGeneratorAsset, int totalCurrentGeneratorAssets) + { + var completedGeneratorProgress = (float)currentGenerator / totalGenerators; + var currentGeneratorProgress = ((float)currentGeneratorAsset / totalCurrentGeneratorAssets) / totalGenerators; + var progressToReport = completedGeneratorProgress + currentGeneratorProgress; + OnProgressChanged?.Invoke(progressToReport); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs.meta new file mode 100644 index 0000000..4b18db3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e5906f8cb3e4eec489a2f7a82844bb3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs new file mode 100644 index 0000000..b548baf --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Threading.Tasks; + +namespace AssetStoreTools.Previews.Generators +{ + internal interface IPreviewGenerator + { + PreviewGenerationSettings Settings { get; } + + event Action OnProgressChanged; + + Task Generate(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs.meta new file mode 100644 index 0000000..c7178a8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ebddaccb94ca6e34aa36b535d0a47249 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs new file mode 100644 index 0000000..760e2d1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs @@ -0,0 +1,362 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using UnityEngine.Tilemaps; + +namespace AssetStoreTools.Previews.Generators +{ + internal class NativePreviewGenerator : PreviewGeneratorBase + { + private const double InitialPreviewLoadingTimeoutSeconds = 10; + + private NativePreviewGenerationSettings _nativeSettings; + + private RenderTexture _renderTexture; + + private int _generatedPreviewsCount; + private int _totalPreviewsCount; + + public override event Action OnProgressChanged; + + public NativePreviewGenerator(NativePreviewGenerationSettings settings) + : base(settings) + { + _nativeSettings = settings; + } + + protected override void Validate() + { + base.Validate(); + + if (_nativeSettings.ChunkSize <= 0) + throw new ArgumentException("Chunk size must be larger than 0"); + } + + protected override async Task GenerateImpl() + { + var result = new PreviewGenerationResult() + { + GenerationType = _nativeSettings.GenerationType + }; + + OnProgressChanged?.Invoke(0f); + + try + { + var objects = GetObjectsRequiringPreviews(_nativeSettings.InputPaths); + var filteredObjects = new List(); + var reusedPreviews = new List(); + FilterObjects(objects, filteredObjects, reusedPreviews); + + _generatedPreviewsCount = 0; + _totalPreviewsCount = objects.Count; + + Directory.CreateDirectory(_nativeSettings.OutputPath); + + var generatedPreviews = new List(); + if (!_nativeSettings.WaitForPreviews) + { + WritePreviewsWithoutWaiting(filteredObjects, out generatedPreviews); + } + else + { + if (_nativeSettings.ChunkedPreviewLoading) + { + await WaitAndWritePreviewsChunked(filteredObjects, generatedPreviews); + } + else + { + await WaitAndWritePreviews(filteredObjects, generatedPreviews); + } + } + + var allPreviews = new List(); + allPreviews.AddRange(generatedPreviews); + allPreviews.AddRange(reusedPreviews); + + result.Success = true; + result.GeneratedPreviews = generatedPreviews; + result.Previews = allPreviews; + } + catch (Exception e) + { + result.Success = false; + result.Exception = e; + } + + return result; + } + + private List GetObjectsRequiringPreviews(string[] inputPaths) + { + var objects = new List(); + var guids = AssetDatabase.FindAssets("", inputPaths); + + foreach (var guid in guids) + { + if (objects.Any(x => x.Guid == guid)) + continue; + + var assetPath = AssetDatabase.GUIDToAssetPath(guid); + var obj = AssetDatabase.LoadAssetAtPath(assetPath); + if (!ShouldHavePreview(obj)) + continue; + + objects.Add(new PreviewMetadata() { Type = GenerationType.Native, Guid = guid }); + } + + return objects; + } + + private void FilterObjects(List objects, List filteredObjects, List reusedPreviews) + { + if (Settings.OverwriteExisting || !CachingService.GetCachedMetadata(out var database)) + { + filteredObjects.AddRange(objects); + return; + } + + foreach (var obj in objects) + { + var matchingEntry = database.Previews.FirstOrDefault(x => + x.Guid == obj.Guid + && x.Type == GenerationType.Native + && x.Exists()); + + if (matchingEntry == null) + { + filteredObjects.Add(obj); + } + else + { + reusedPreviews.Add(matchingEntry); + } + } + } + + private bool ShouldHavePreview(UnityEngine.Object asset) + { + if (asset == null) + return false; + + if (!AssetDatabase.IsMainAsset(asset)) + return false; + + switch (asset) + { + case AudioClip _: + case Material _: + case Mesh _: + case TerrainLayer _: + case Texture _: + case Tile _: + return true; + case GameObject go: + var renderers = go.GetComponentsInChildren(); + return renderers != null && renderers.Length > 0; + default: + return false; + } + } + + private PreviewMetadata WritePreviewToDisk(PreviewMetadata metadata, Texture2D texture) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(metadata.Guid)); + var width = Mathf.Min(texture.width, 128); + var height = Mathf.Min(texture.height, 128); + var readableTexture = GraphicsUtility.ResizeTexture(texture, width, height); + var fileName = PreviewConvertUtility.ConvertFilenameWithExtension(asset, _nativeSettings.PreviewFileNamingFormat, _nativeSettings.Format); + var filePath = $"{_nativeSettings.OutputPath}/{fileName}"; + var bytes = PreviewConvertUtility.ConvertTexture(readableTexture, _nativeSettings.Format); + + File.WriteAllBytes(filePath, bytes); + + metadata.Type = GenerationType.Native; + metadata.Name = asset.name; + metadata.Path = filePath; + + return metadata; + } + + private void WritePreviewsWithoutWaiting(List objects, out List generatedPreviews) + { + generatedPreviews = new List(); + + foreach (var obj in objects) + { + var texture = GetAssetPreviewFromGuid(obj.Guid); + if (texture == null) + continue; + + var generatedPreview = WritePreviewToDisk(obj, texture); + generatedPreviews.Add(generatedPreview); + } + } + + private Texture2D GetAssetPreviewFromGuid(string guid) + { + var method = typeof(AssetPreview).GetMethod("GetAssetPreviewFromGUID", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(string) }, null); + var args = new object[] { guid }; + + return method?.Invoke(null, args) as Texture2D; + } + + private async Task WaitAndWritePreviewsChunked(List objects, List generatedPreviews) + { + var chunks = objects.Count / _nativeSettings.ChunkSize; + var remainder = objects.Count % _nativeSettings.ChunkSize; + if (remainder != 0) + chunks += 1; + + for (int i = 0; i < chunks; i++) + { + var chunkObjects = new List(); + + for (int j = 0; j < _nativeSettings.ChunkSize; j++) + { + var index = i * _nativeSettings.ChunkSize + j; + if (index == objects.Count) + break; + + chunkObjects.Add(objects[index]); + } + + var generatedPreviewsChunk = new List(); + await WaitAndWritePreviews(chunkObjects, generatedPreviewsChunk); + generatedPreviews.AddRange(generatedPreviewsChunk); + } + } + + private async Task WaitAndWritePreviews(List objects, List generatedPreviews) + { + var initialObjectCount = objects.Count(); + if (initialObjectCount == 0) + return; + + await WaitAndWritePreviewIteration(objects, generatedPreviews); + var remainingObjectCount = objects.Count; + + // First iteration may take longer to start loading objects + var firstIterationStartTime = EditorApplication.timeSinceStartup; + while (true) + { + if (remainingObjectCount < initialObjectCount) + break; + + if (EditorApplication.timeSinceStartup - firstIterationStartTime > InitialPreviewLoadingTimeoutSeconds) + throw new Exception("Preview loading timed out."); + + await WaitAndWritePreviewIteration(objects, generatedPreviews); + remainingObjectCount = objects.Count; + } + + if (remainingObjectCount == 0) + return; + + while (true) + { + await WaitForEndOfFrame(1); + await WaitAndWritePreviewIteration(objects, generatedPreviews); + + // If no more previews are being loaded, try one more time before quitting + if (objects.Count == remainingObjectCount) + { + await WaitForEndOfFrame(1); + await WaitAndWritePreviewIteration(objects, generatedPreviews); + + if (objects.Count == remainingObjectCount) + { + var missingObjects = string.Join("\n", objects.Select(x => AssetDatabase.GUIDToAssetPath(x.Guid))); + Debug.LogWarning($"Unity Editor failed to fetch previews for {objects.Count} objects:\n{missingObjects}"); + break; + } + } + + remainingObjectCount = objects.Count; + + // Exit when all previews are loaded + if (remainingObjectCount == 0) + break; + } + } + + private async Task WaitAndWritePreviewIteration(List objects, List generatedPreviews) + { + var cacheSize = Mathf.Max(_nativeSettings.ChunkSize * 2, objects.Count() + _nativeSettings.ChunkSize); + AssetPreview.SetPreviewTextureCacheSize(cacheSize); + + // Initial queueing + foreach (var obj in objects) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(obj.Guid)); + AssetPreview.GetAssetPreview(asset); + } + + await WaitForEndOfFrame(); + + // Waiting (NOTE: works inconsistently across Unity streams) + foreach (var obj in objects) + { + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(obj.Guid)); + if (AssetPreview.IsLoadingAssetPreview(asset.GetInstanceID())) + { + await WaitForEndOfFrame(); + } + } + + // Writing + for (int i = 0; i < objects.Count; i++) + { + var obj = objects[i]; + + var asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(obj.Guid)); + var texture = AssetPreview.GetAssetPreview(asset); + if (texture == null) + continue; + + WritePreviewToDisk(obj, texture); + generatedPreviews.Add(obj); + _generatedPreviewsCount++; + OnProgressChanged?.Invoke((float)_generatedPreviewsCount / _totalPreviewsCount); + } + + // Removing written objects from the list + for (int i = objects.Count - 1; i >= 0; i--) + { + if (objects[i].Exists()) + objects.RemoveAt(i); + } + } + + private async Task WaitForEndOfFrame(double atLeastSeconds) + { + var startTime = EditorApplication.timeSinceStartup; + while (EditorApplication.timeSinceStartup - startTime <= atLeastSeconds) + { + await WaitForEndOfFrame(); + } + } + + private async Task WaitForEndOfFrame() + { + var isNextFrame = false; + EditorApplication.CallbackFunction callback = null; + callback = () => + { + EditorApplication.update -= callback; + isNextFrame = true; + }; + + EditorApplication.update += callback; + while (!isNextFrame) + await Task.Yield(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs.meta new file mode 100644 index 0000000..0d6454d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dd6eeb2f97a2ed34db51ab5ac0b3ffa1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs new file mode 100644 index 0000000..02d779d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs @@ -0,0 +1,45 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Services; +using System; +using System.Threading.Tasks; + +namespace AssetStoreTools.Previews.Generators +{ + internal abstract class PreviewGeneratorBase : IPreviewGenerator + { + public PreviewGenerationSettings Settings { get; } + protected ICachingService CachingService; + + public abstract event Action OnProgressChanged; + + public PreviewGeneratorBase(PreviewGenerationSettings settings) + { + Settings = settings; + CachingService = PreviewServiceProvider.Instance.GetService(); + } + + public async Task Generate() + { + Validate(); + + var result = await GenerateImpl(); + if (result.Success) + { + CachingService.CacheMetadata(result.GeneratedPreviews); + } + + return result; + } + + protected virtual void Validate() + { + if (Settings.InputPaths == null || Settings.InputPaths.Length == 0) + throw new ArgumentException("Input paths cannot be null"); + + if (string.IsNullOrEmpty(Settings.OutputPath)) + throw new ArgumentException("Output path cannot be null"); + } + + protected abstract Task GenerateImpl(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs.meta new file mode 100644 index 0000000..36e64a2 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cedf01448e0edcc4fb55f19f2e92b740 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services.meta new file mode 100644 index 0000000..55ebe0c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aa18c820f185bfc4d8cd59e3418e2c4e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching.meta new file mode 100644 index 0000000..449835a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eb61a60f2ff91a448a7808ef2a25f871 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs new file mode 100644 index 0000000..10bb448 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs @@ -0,0 +1,87 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Utility; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace AssetStoreTools.Previews.Services +{ + internal class CachingService : ICachingService + { + public void CacheMetadata(IEnumerable previews) + { + var updatedDatabase = UpdatePreviewDatabase(previews); + + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = PreviewDatabaseContractResolver.Instance, + Converters = new List() { new StringEnumConverter() }, + Formatting = Formatting.Indented + }; + + CacheUtil.CreateFileInTempCache(Constants.Previews.PreviewDatabaseFile, JsonConvert.SerializeObject(updatedDatabase, serializerSettings), true); + } + + public bool GetCachedMetadata(out PreviewDatabase previewDatabase) + { + previewDatabase = null; + if (!CacheUtil.GetFileFromTempCache(Constants.Previews.PreviewDatabaseFile, out string filePath)) + return false; + + try + { + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = PreviewDatabaseContractResolver.Instance, + Converters = new List() { new StringEnumConverter() } + }; + + previewDatabase = JsonConvert.DeserializeObject(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings); + return true; + } + catch + { + return false; + } + } + + private PreviewDatabase UpdatePreviewDatabase(IEnumerable previews) + { + PreviewDatabase database; + if (!GetCachedMetadata(out database)) + database = new PreviewDatabase(); + + // Delete missing previews + for (int i = database.Previews.Count - 1; i >= 0; i--) + { + if (database.Previews[i].Exists()) + continue; + + database.Previews.RemoveAt(i); + } + + // Append new previews & Replace existing previews + foreach (var preview in previews) + { + var matchingPreviews = database.Previews.Where(x => x.Guid == preview.Guid).ToList(); + foreach (var matchingPreview in matchingPreviews) + { + // Delete previously generated preview of the same type + if (matchingPreview.Type == preview.Type) + database.Previews.Remove(matchingPreview); + // Delete previously generated preview of a different type if the path matches + else if (matchingPreview.Path.Equals(preview.Path)) + database.Previews.Remove(matchingPreview); + } + + database.Previews.Add(preview); + } + + database.Previews = database.Previews.OrderBy(x => x.Guid).ThenBy(x => x.Type).ToList(); + return database; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs.meta new file mode 100644 index 0000000..ef9417e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e0b6cf909c8798b4590744959571a21f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/CachingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs new file mode 100644 index 0000000..6713000 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs @@ -0,0 +1,11 @@ +using AssetStoreTools.Previews.Data; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.Services +{ + internal interface ICachingService : IPreviewService + { + void CacheMetadata(IEnumerable previews); + bool GetCachedMetadata(out PreviewDatabase previewDatabase); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs.meta new file mode 100644 index 0000000..50b3339 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eeaeae010299dcd489adb00dbf51b274 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/Caching/ICachingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs new file mode 100644 index 0000000..0bc56ae --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs @@ -0,0 +1,4 @@ +namespace AssetStoreTools.Previews.Services +{ + public interface IPreviewService { } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs.meta new file mode 100644 index 0000000..28043dc --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8c2761fe05638644d8e3a265865beef8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/IPreviewService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs new file mode 100644 index 0000000..7b2754b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs @@ -0,0 +1,17 @@ +using AssetStoreTools.Utility; + +namespace AssetStoreTools.Previews.Services +{ + internal class PreviewServiceProvider : ServiceProvider + { + public static PreviewServiceProvider Instance => _instance ?? (_instance = new PreviewServiceProvider()); + private static PreviewServiceProvider _instance; + + private PreviewServiceProvider() { } + + protected override void RegisterServices() + { + Register(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs.meta new file mode 100644 index 0000000..0f5345e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a19bf5a4e3e441047bbc1b894e2a1149 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Services/PreviewServiceProvider.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI.meta new file mode 100644 index 0000000..67d04e3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4738f3648c8368244a968bc840c1152b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data.meta new file mode 100644 index 0000000..b302df5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 23a2f4eadd444194a91ff4ce509e4798 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs new file mode 100644 index 0000000..1b2fee9 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs @@ -0,0 +1,56 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.IO; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal class AssetPreview : IAssetPreview + { + private PreviewMetadata _metadata; + + private UnityEngine.Object _cachedAsset; + private string _cachedAssetPath; + private Texture2D _cachedTexture; + + public UnityEngine.Object Asset => _cachedAsset ?? (_cachedAsset = AssetDatabase.LoadAssetAtPath(AssetPath)); + public string AssetPath => _cachedAssetPath ?? (_cachedAssetPath = AssetDatabase.GUIDToAssetPath(_metadata.Guid)); + + public AssetPreview(PreviewMetadata metadata) + { + _metadata = metadata; + } + + public string GetAssetPath() + { + var assetPath = AssetDatabase.GUIDToAssetPath(_metadata.Guid); + return assetPath; + } + + public async Task LoadImage(Action onSuccess) + { + if (_cachedTexture == null) + { + if (!_metadata.Exists()) + return; + + await Task.Yield(); + + try + { + _cachedTexture = new Texture2D(1, 1); + _cachedTexture.LoadImage(File.ReadAllBytes(_metadata.Path)); + } + catch (Exception e) + { + Debug.LogException(e); + return; + } + } + + onSuccess?.Invoke(_cachedTexture); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs.meta new file mode 100644 index 0000000..9764acf --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 739cf05c689204f4089fd0a6bddb8c3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreview.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs new file mode 100644 index 0000000..40db2b6 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs @@ -0,0 +1,46 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal class AssetPreviewCollection : IAssetPreviewCollection + { + private GenerationType _generationType; + private List _images; + + public event Action OnCollectionChanged; + + public AssetPreviewCollection() + { + _images = new List(); + } + + public GenerationType GetGenerationType() + { + return _generationType; + } + + public IEnumerable GetPreviews() + { + return _images; + } + + public void Refresh(GenerationType generationType, IEnumerable previews) + { + _images.Clear(); + + _generationType = generationType; + + foreach (var entry in previews) + { + if (!entry.Exists()) + continue; + + _images.Add(new AssetPreview(entry)); + } + + OnCollectionChanged?.Invoke(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs.meta new file mode 100644 index 0000000..319746a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9b1a0db8710933048b49dcca463fb8fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/AssetPreviewCollection.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs new file mode 100644 index 0000000..f7fc750 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; +using UnityEngine; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal interface IAssetPreview + { + UnityEngine.Object Asset { get; } + string GetAssetPath(); + Task LoadImage(Action onSuccess); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs.meta new file mode 100644 index 0000000..3bf6b86 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0f9373dfc16d0fa4794dac29b75204ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreview.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs new file mode 100644 index 0000000..6e614c6 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs @@ -0,0 +1,15 @@ +using AssetStoreTools.Previews.Data; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal interface IAssetPreviewCollection + { + event Action OnCollectionChanged; + + GenerationType GetGenerationType(); + IEnumerable GetPreviews(); + void Refresh(GenerationType generationType, IEnumerable previews); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs.meta new file mode 100644 index 0000000..60da018 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fc9d9abd80c070f44ac49d5ce23d2fc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IAssetPreviewCollection.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs new file mode 100644 index 0000000..51f626c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs @@ -0,0 +1,27 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal interface IPreviewGeneratorSettings + { + event Action OnGenerationTypeChanged; + event Action OnGenerationPathsChanged; + + void LoadSettings(PreviewGenerationSettings settings); + + GenerationType GetGenerationType(); + void SetGenerationType(GenerationType type); + List GetAvailableGenerationTypes(); + + List GetGenerationPaths(); + void AddGenerationPath(string path); + void RemoveGenerationPath(string path); + void ClearGenerationPaths(); + bool IsGenerationPathValid(string path, out string error); + + IPreviewGenerator CreateGenerator(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs.meta new file mode 100644 index 0000000..e3ecce7 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 55c9fcde15f06754588fd02fb8b99a60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/IPreviewGeneratorSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs new file mode 100644 index 0000000..86962e4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs @@ -0,0 +1,212 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using AssetStoreTools.Utility; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace AssetStoreTools.Previews.UI.Data +{ + internal class PreviewGeneratorSettings : IPreviewGeneratorSettings + { + private readonly GenerationType[] _availableGenerationTypes = new GenerationType[] + { + GenerationType.Native, + GenerationType.Custom + }; + + private List _inputPaths; + private GenerationType _generationType; + + public event Action OnGenerationTypeChanged; + public event Action OnGenerationPathsChanged; + + public PreviewGeneratorSettings() + { + _inputPaths = new List(); + _generationType = GenerationType.Native; + } + + public void LoadSettings(PreviewGenerationSettings settings) + { + if (settings == null) + return; + + _inputPaths = settings.InputPaths.ToList(); + OnGenerationPathsChanged?.Invoke(); + + switch (settings) + { + case NativePreviewGenerationSettings _: + _generationType = GenerationType.Native; + break; + case CustomPreviewGenerationSettings _: + _generationType = GenerationType.Custom; + break; + default: + return; + } + + OnGenerationTypeChanged?.Invoke(); + } + + public GenerationType GetGenerationType() + { + return _generationType; + } + + public void SetGenerationType(GenerationType type) + { + _generationType = type; + OnGenerationTypeChanged?.Invoke(); + } + + public List GetAvailableGenerationTypes() + { + return _availableGenerationTypes.ToList(); + } + + public List GetGenerationPaths() + { + return _inputPaths; + } + + public void AddGenerationPath(string path) + { + if (string.IsNullOrEmpty(path)) + return; + + if (_inputPaths.Contains(path)) + return; + + // Prevent redundancy for new paths + var existingPath = _inputPaths.FirstOrDefault(x => path.StartsWith(x + "/")); + if (existingPath != null) + { + Debug.LogWarning($"Path '{path}' is already included with existing path: '{existingPath}'"); + return; + } + + // Prevent redundancy for already added paths + var redundantPaths = _inputPaths.Where(x => x.StartsWith(path + "/")).ToArray(); + foreach (var redundantPath in redundantPaths) + { + Debug.LogWarning($"Existing validation path '{redundantPath}' has been made redundant by the inclusion of new validation path: '{path}'"); + _inputPaths.Remove(redundantPath); + } + + _inputPaths.Add(path); + + OnGenerationPathsChanged?.Invoke(); + } + + public void RemoveGenerationPath(string path) + { + if (!_inputPaths.Contains(path)) + return; + + _inputPaths.Remove(path); + + OnGenerationPathsChanged?.Invoke(); + } + + public void ClearGenerationPaths() + { + if (_inputPaths.Count == 0) + return; + + _inputPaths.Clear(); + + OnGenerationPathsChanged?.Invoke(); + } + + public bool IsGenerationPathValid(string path, out string error) + { + error = string.Empty; + + if (string.IsNullOrEmpty(path)) + { + error = "Path cannot be empty"; + return false; + } + + var isAssetsPath = path.StartsWith("Assets/") + || path.Equals("Assets"); + var isPackagePath = PackageUtility.GetPackageByManifestPath($"{path}/package.json", out _); + + if (!isAssetsPath && !isPackagePath) + { + error = "Selected path must be within the Assets folder or point to a root path of a package"; + return false; + } + + if (!Directory.Exists(path)) + { + error = "Path does not exist"; + return false; + } + + if (path.Split('/').Any(x => x.StartsWith(".") || x.EndsWith("~"))) + { + error = $"Path '{path}' cannot be selected as it is a hidden folder and not part of the Asset Database"; + return false; + } + + return true; + } + + public IPreviewGenerator CreateGenerator() + { + switch (_generationType) + { + case GenerationType.Native: + return CreateNativeGenerator(); + case GenerationType.Custom: + return CreateCustomGenerator(); + default: + throw new NotImplementedException("Undefined generator type"); + } + } + + private IPreviewGenerator CreateNativeGenerator() + { + var settings = new NativePreviewGenerationSettings() + { + InputPaths = _inputPaths.ToArray(), + OutputPath = Constants.Previews.Native.DefaultOutputPath, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + Format = Constants.Previews.Native.DefaultFormat, + WaitForPreviews = Constants.Previews.Native.DefaultWaitForPreviews, + ChunkedPreviewLoading = Constants.Previews.Native.DefaultChunkedPreviewLoading, + ChunkSize = Constants.Previews.Native.DefaultChunkSize, + OverwriteExisting = true + }; + + return new NativePreviewGenerator(settings); + } + + private IPreviewGenerator CreateCustomGenerator() + { + var settings = new CustomPreviewGenerationSettings() + { + InputPaths = _inputPaths.ToArray(), + OutputPath = Constants.Previews.Custom.DefaultOutputPath, + Width = Constants.Previews.Custom.DefaultWidth, + Height = Constants.Previews.Custom.DefaultHeight, + Depth = Constants.Previews.Custom.DefaultDepth, + NativeWidth = Constants.Previews.Custom.DefaultNativeWidth, + NativeHeight = Constants.Previews.Custom.DefaultNativeHeight, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + Format = Constants.Previews.Custom.DefaultFormat, + AudioSampleColor = Constants.Previews.Custom.DefaultAudioSampleColor, + AudioBackgroundColor = Constants.Previews.Custom.DefaultAudioBackgroundColor, + OverwriteExisting = true + }; + + var generator = new CustomPreviewGenerator(settings); + return generator; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs.meta new file mode 100644 index 0000000..b57c82a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9e6f754b1179d8d4cb40f62692619a63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Data/PreviewGeneratorSettings.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements.meta new file mode 100644 index 0000000..d81acd5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 700ec0107b011824892281e880281bb1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs new file mode 100644 index 0000000..aee6fa8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs @@ -0,0 +1,83 @@ +using AssetStoreTools.Previews.UI.Data; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class AssetPreviewElement : VisualElement + { + // Data + private IAssetPreview _assetPreview; + + // UI + private Image _image; + private Label _label; + + public AssetPreviewElement() + { + AddToClassList("preview-list-image"); + + Create(); + + RegisterCallback(OnImageClicked); + } + + private void Create() + { + CreateFiller(); + CreateImage(); + CreateLabel(); + } + + private void CreateImage() + { + _image = new Image(); + Add(_image); + } + + private void CreateFiller() + { + var filler = new VisualElement() { name = "Filler" }; + Add(filler); + } + + private void CreateLabel() + { + _label = new Label(); + Add(_label); + } + + private void SetImage(Texture2D texture) + { + _image.style.width = texture.width < 128 ? texture.width : 128; + _image.style.height = texture.height < 128 ? texture.height : 128; + _image.style.backgroundImage = texture; + } + + private void OnImageClicked(MouseDownEvent _) + { + EditorGUIUtility.PingObject(_assetPreview.Asset); + } + + public void SetSource(IAssetPreview assetPreview) + { + _assetPreview = assetPreview; + _assetPreview.LoadImage(SetImage); + + var assetPath = _assetPreview.GetAssetPath(); + + if (string.IsNullOrEmpty(assetPath)) + { + _label.text = "[Missing]"; + tooltip = "This asset has been deleted"; + return; + } + + var assetNameWithExtension = assetPath.Split('/').Last(); + _label.text = assetNameWithExtension; + tooltip = assetPath; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs.meta new file mode 100644 index 0000000..fec756c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 28891b8cff841a44eb508494d62c190c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/AssetPreviewElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs new file mode 100644 index 0000000..a1bf53a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class GridListElement : VisualElement + { + public int ElementWidth; + public int ElementHeight; + private int _visibilityHeadroom => ElementHeight; + + public IList ItemSource; + public Func MakeItem; + public Action BindItem; + + private ScrollView _scrollView; + + public GridListElement() + { + style.flexGrow = 1; + + Create(); + + _scrollView.contentViewport.RegisterCallback(OnGeometryChanged); + _scrollView.verticalScroller.valueChanged += OnVerticalScroll; +#if UNITY_2021_1_OR_NEWER + _scrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden; +#else + _scrollView.showHorizontal = false; +#endif + } + + private void Create() + { + _scrollView = new ScrollView(); + Add(_scrollView); + } + + private void OnGeometryChanged(GeometryChangedEvent evt) + { + Redraw(); + } + + private void OnVerticalScroll(float value) + { + Redraw(); + } + + public void Redraw() + { + if (ElementWidth == 0 + || ElementHeight == 0 + || ItemSource == null + || MakeItem == null + || BindItem == null) + return; + + _scrollView.Clear(); + + var rowCapacity = Mathf.FloorToInt(_scrollView.contentContainer.worldBound.width / ElementWidth); + if (rowCapacity == 0) + rowCapacity = 1; + + var totalRequiredRows = ItemSource.Count / rowCapacity; + if (ItemSource.Count % rowCapacity != 0) + totalRequiredRows++; + + _scrollView.contentContainer.style.height = totalRequiredRows * ElementHeight; + + var visibleRows = new List(); + for (int i = 0; i < totalRequiredRows; i++) + { + var visible = IsRowVisible(i); + if (!visible) + continue; + + var rowElement = CreateRow(i); + + for (int j = 0; j < rowCapacity; j++) + { + var elementIndex = i * rowCapacity + j; + if (elementIndex >= ItemSource.Count) + { + rowElement.Add(CreateFillerElement()); + continue; + } + + var element = MakeItem?.Invoke(); + BindItem?.Invoke(element, elementIndex); + + rowElement.Add(element); + } + + _scrollView.Add(rowElement); + } + } + + private bool IsRowVisible(int rowIndex) + { + var contentStartY = _scrollView.contentContainer.worldBound.yMin; + var visibleContentMinY = _scrollView.contentViewport.worldBound.yMin - _visibilityHeadroom; + var visibleContentMaxY = _scrollView.contentViewport.worldBound.yMax + _visibilityHeadroom; + if (_scrollView.contentViewport.worldBound.height == 0) + visibleContentMaxY = this.worldBound.yMax; + + var rowMinY = (rowIndex * ElementHeight) + contentStartY; + var rowMaxY = (rowIndex * ElementHeight) + ElementHeight + contentStartY; + + var fullyVisible = rowMinY >= visibleContentMinY && rowMaxY <= visibleContentMaxY; + var partiallyAbove = rowMinY < visibleContentMinY && rowMaxY > visibleContentMinY; + var partiallyBelow = rowMaxY > visibleContentMaxY && rowMinY < visibleContentMaxY; + + return fullyVisible || partiallyAbove || partiallyBelow; + } + + private VisualElement CreateRow(int rowIndex) + { + var rowElement = new VisualElement() { name = $"Row {rowIndex}" }; + rowElement.style.flexDirection = FlexDirection.Row; + rowElement.style.position = Position.Absolute; + rowElement.style.top = ElementHeight * rowIndex; + rowElement.style.width = _scrollView.contentViewport.worldBound.width; + rowElement.style.justifyContent = Justify.SpaceAround; + + return rowElement; + } + + private VisualElement CreateFillerElement() + { + var element = new VisualElement(); + element.style.width = ElementWidth; + element.style.height = ElementHeight; + + return element; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs.meta new file mode 100644 index 0000000..a439c55 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 81d9f779e8c2a464cbdc1e39a4864803 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/GridListElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs new file mode 100644 index 0000000..28a35b1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs @@ -0,0 +1,116 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.UI.Data; +using System.Linq; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.SceneManagement; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewCollectionElement : VisualElement + { + // Data + private IAssetPreviewCollection _collection; + + // UI + private Label _previewCountLabel; + private GridListElement _gridListElement; + + public PreviewCollectionElement(IAssetPreviewCollection collection) + { + AddToClassList("preview-list"); + + _collection = collection; + _collection.OnCollectionChanged += RefreshList; + + Create(); + RefreshList(); + + SubscribeToSceneChanges(); + } + + private void Create() + { + CreateLabel(); + CreateGridListElement(); + } + + private void CreateLabel() + { + _previewCountLabel = new Label(); + _previewCountLabel.style.display = DisplayStyle.None; + Add(_previewCountLabel); + } + + private void CreateGridListElement() + { + _gridListElement = new GridListElement(); + _gridListElement.MakeItem = CreatePreview; + _gridListElement.BindItem = BindPreview; + _gridListElement.ElementWidth = 140 + 10; // Accounting for margin style + _gridListElement.ElementHeight = 160 + 10; // Accounting for margin style + Add(_gridListElement); + } + + private VisualElement CreatePreview() + { + var preview = new AssetPreviewElement(); + return preview; + } + + private void BindPreview(VisualElement element, int index) + { + var previewElement = (AssetPreviewElement)element; + var preview = _collection.GetPreviews().ToList()[index]; + previewElement.SetSource(preview); + } + + private void RefreshList() + { + var type = _collection.GetGenerationType(); + var items = _collection.GetPreviews().ToList(); + _previewCountLabel.text = $"Displaying {items.Count} {ConvertGenerationTypeName(type)} previews"; + _previewCountLabel.style.display = DisplayStyle.Flex; + _previewCountLabel.style.alignSelf = Align.Center; + _previewCountLabel.style.marginBottom = 10; + _previewCountLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + + _gridListElement.ItemSource = items; + _gridListElement.Redraw(); + } + + private string ConvertGenerationTypeName(GenerationType type) + { + switch (type) + { + case GenerationType.Custom: + return "high resolution"; + default: + return type.ToString().ToLower(); + } + } + + private void SubscribeToSceneChanges() + { + var windowToSubscribeTo = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + UnityAction sceneChanged = null; + sceneChanged = new UnityAction((_, __) => RefreshObjects(windowToSubscribeTo)); + EditorSceneManager.activeSceneChangedInEditMode += sceneChanged; + + void RefreshObjects(PreviewGeneratorWindow subscribedWindow) + { + // Remove callback if preview generator window instance changed + var activeWindow = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + if (subscribedWindow == null || subscribedWindow != activeWindow) + { + EditorSceneManager.activeSceneChangedInEditMode -= sceneChanged; + return; + } + + RefreshList(); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs.meta new file mode 100644 index 0000000..fc6e1c1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 842a11e046ca5284d9de9f4a05b1fa26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewCollectionElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs new file mode 100644 index 0000000..3d2f9a8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs @@ -0,0 +1,50 @@ +using AssetStoreTools.Previews.UI.Data; +using System; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewGenerateButtonElement : VisualElement + { + // Data + private IPreviewGeneratorSettings _settings; + + // UI + private Button _generateButton; + + public event Action OnGenerate; + + public PreviewGenerateButtonElement(IPreviewGeneratorSettings settings) + { + _settings = settings; + _settings.OnGenerationPathsChanged += GenerationPathsChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + _generateButton = new Button(Validate) { text = "Generate" }; + _generateButton.AddToClassList("preview-generate-button"); + + Add(_generateButton); + } + + private void Validate() + { + OnGenerate?.Invoke(); + } + + private void GenerationPathsChanged() + { + var inputPathsPresent = _settings.GetGenerationPaths().Count > 0; + _generateButton.SetEnabled(inputPathsPresent); + } + + private void Deserialize() + { + GenerationPathsChanged(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs.meta new file mode 100644 index 0000000..57303ac --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1c8fbb0b13ba7d3479c0867c440821e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGenerateButtonElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs new file mode 100644 index 0000000..cf72440 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs @@ -0,0 +1,122 @@ +using AssetStoreTools.Previews.UI.Data; +using AssetStoreTools.Utility; +using UnityEditor; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Validator.UI.Elements +{ + internal class PreviewGeneratorPathsElement : VisualElement + { + // Data + private IPreviewGeneratorSettings _settings; + + // UI + private ScrollView _pathBoxScrollView; + + public PreviewGeneratorPathsElement(IPreviewGeneratorSettings settings) + { + AddToClassList("preview-paths"); + + _settings = settings; + _settings.OnGenerationPathsChanged += InputPathsChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + var pathSelectionRow = new VisualElement(); + pathSelectionRow.AddToClassList("preview-settings-selection-row"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("preview-settings-selection-label-help-row"); + labelHelpRow.style.alignSelf = Align.FlexStart; + + Label pathLabel = new Label { text = "Input paths" }; + Image pathLabelTooltip = new Image + { + tooltip = "Select the folder (or multiple folders) to generate asset previews for." + }; + + labelHelpRow.Add(pathLabel); + labelHelpRow.Add(pathLabelTooltip); + + var fullPathBox = new VisualElement() { name = "PreviewPaths" }; + fullPathBox.AddToClassList("preview-paths-box"); + + _pathBoxScrollView = new ScrollView { name = "PreviewPathsScrollView" }; + _pathBoxScrollView.AddToClassList("preview-paths-scroll-view"); + + VisualElement scrollViewBottomRow = new VisualElement(); + scrollViewBottomRow.AddToClassList("preview-paths-scroll-view-bottom-row"); + + var addExtraPathsButton = new Button(BrowsePath) { text = "Add a path" }; + addExtraPathsButton.AddToClassList("preview-paths-add-button"); + scrollViewBottomRow.Add(addExtraPathsButton); + + fullPathBox.Add(_pathBoxScrollView); + fullPathBox.Add(scrollViewBottomRow); + + pathSelectionRow.Add(labelHelpRow); + pathSelectionRow.Add(fullPathBox); + + Add(pathSelectionRow); + } + + private VisualElement CreateSinglePathElement(string path) + { + var validationPath = new VisualElement(); + validationPath.AddToClassList("preview-paths-path-row"); + + var folderPathLabel = new Label(path); + folderPathLabel.AddToClassList("preview-paths-path-row-input-field"); + + var removeButton = new Button(() => + { + _settings.RemoveGenerationPath(path); + }); + removeButton.text = "X"; + removeButton.AddToClassList("preview-paths-path-row-remove-button"); + + validationPath.Add(folderPathLabel); + validationPath.Add(removeButton); + + return validationPath; + } + + private void BrowsePath() + { + string absolutePath = EditorUtility.OpenFolderPanel("Select a directory", "Assets", ""); + + if (string.IsNullOrEmpty(absolutePath)) + return; + + var relativePath = FileUtility.AbsolutePathToRelativePath(absolutePath, ASToolsPreferences.Instance.EnableSymlinkSupport); + + if (!_settings.IsGenerationPathValid(relativePath, out var error)) + { + EditorUtility.DisplayDialog("Invalid path", error, "OK"); + return; + } + + _settings.AddGenerationPath(relativePath); + } + + private void InputPathsChanged() + { + var inputPaths = _settings.GetGenerationPaths(); + + _pathBoxScrollView.Clear(); + foreach (var path in inputPaths) + { + _pathBoxScrollView.Add(CreateSinglePathElement(path)); + } + } + + private void Deserialize() + { + InputPathsChanged(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs.meta new file mode 100644 index 0000000..efdee4a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cd3e3f7fbfc5f1e46835438be2756746 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorPathsElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs new file mode 100644 index 0000000..d5268f5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs @@ -0,0 +1,99 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.UI.Data; +using AssetStoreTools.Validator.UI.Elements; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewGeneratorSettingsElement : VisualElement + { + // Data + private IPreviewGeneratorSettings _settings; + + // UI + private PreviewGeneratorPathsElement _previewPathsElement; + private ToolbarMenu _generationTypeMenu; + + public PreviewGeneratorSettingsElement(IPreviewGeneratorSettings settings) + { + AddToClassList("preview-settings"); + + _settings = settings; + _settings.OnGenerationTypeChanged += GenerationTypeChanged; + + Create(); + Deserialize(); + } + + private void Create() + { + CreateGenerationType(); + CreateInputPathsElement(); + } + + private void CreateInputPathsElement() + { + _previewPathsElement = new PreviewGeneratorPathsElement(_settings); + Add(_previewPathsElement); + } + + private void CreateGenerationType() + { + var typeSelectionBox = new VisualElement(); + typeSelectionBox.AddToClassList("preview-settings-selection-row"); + + VisualElement labelHelpRow = new VisualElement(); + labelHelpRow.AddToClassList("preview-settings-selection-label-help-row"); + + Label generationTypeLabel = new Label { text = "Generation type" }; + Image categoryLabelTooltip = new Image + { + tooltip = "Choose the generation type for your previews.\n\n" + + "- Native: retrieve previews from the Asset Database which are generated by Unity Editor internally\n" + + "- High Resolution (experimental): generate previews using a custom implementation. Resulting previews are of higher resolution " + + "than those generated by Unity Editor. Note that they may look slightly different from native previews" + }; + + labelHelpRow.Add(generationTypeLabel); + labelHelpRow.Add(categoryLabelTooltip); + + _generationTypeMenu = new ToolbarMenu { name = "GenerationTypeMenu" }; + _generationTypeMenu.AddToClassList("preview-settings-selection-dropdown"); + + typeSelectionBox.Add(labelHelpRow); + typeSelectionBox.Add(_generationTypeMenu); + + // Append available categories + var types = _settings.GetAvailableGenerationTypes(); + foreach (var t in types) + { + _generationTypeMenu.menu.AppendAction(ConvertGenerationTypeName(t), _ => _settings.SetGenerationType(t)); + } + + Add(typeSelectionBox); + } + + private string ConvertGenerationTypeName(GenerationType type) + { + switch (type) + { + case GenerationType.Custom: + return "High Resolution (experimental)"; + default: + return type.ToString(); + } + } + + private void GenerationTypeChanged() + { + var t = _settings.GetGenerationType(); + _generationTypeMenu.text = ConvertGenerationTypeName(t); + } + + private void Deserialize() + { + GenerationTypeChanged(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs.meta new file mode 100644 index 0000000..acda521 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6f38de8a438b8c94a81fe5f2cc45c110 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewGeneratorSettingsElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs new file mode 100644 index 0000000..1306f05 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs @@ -0,0 +1,87 @@ +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Elements +{ + internal class PreviewWindowDescriptionElement : VisualElement + { + private const string DescriptionFoldoutText = "Generate and inspect asset preview images to be displayed in your package listing page on the Asset Store."; + private const string DescriptionFoldoutContentText = "Images generated in this window will be reused when exporting a package. Any missing images generated during the package export process will also appear here.\n\n" + + "Preview images are displayed on the Asset Store under the 'Package Content' section of the package listing. " + + "They are also displayed in the package importer window that appears during the package import process. " + + "Note that these images will not replace the images used for the assets in the Project window after the package gets imported."; + + private VisualElement _descriptionSimpleContainer; + private Button _showMoreButton; + + private VisualElement _descriptionFullContainer; + private Button _showLessButton; + + public PreviewWindowDescriptionElement() + { + AddToClassList("asset-preview-description"); + Create(); + } + + private void Create() + { + CreateSimpleDescription(); + CreateFullDescription(); + } + + private void CreateSimpleDescription() + { + _descriptionSimpleContainer = new VisualElement(); + _descriptionSimpleContainer.AddToClassList("asset-preview-description-simple-container"); + + var simpleDescription = new Label(DescriptionFoldoutText); + simpleDescription.AddToClassList("asset-preview-description-simple-label"); + + _showMoreButton = new Button(ToggleFullDescription) { text = "Show more..." }; + _showMoreButton.AddToClassList("asset-preview-description-hyperlink-button"); + _showMoreButton.AddToClassList("asset-preview-description-show-button"); + + _descriptionSimpleContainer.Add(simpleDescription); + _descriptionSimpleContainer.Add(_showMoreButton); + + Add(_descriptionSimpleContainer); + } + + private void CreateFullDescription() + { + _descriptionFullContainer = new VisualElement(); + _descriptionFullContainer.AddToClassList("asset-preview-description-full-container"); + + var validatorDescription = new Label() + { + text = DescriptionFoldoutContentText + }; + validatorDescription.AddToClassList("asset-preview-description-full-label"); + + _showLessButton = new Button(ToggleFullDescription) { text = "Show less..." }; + _showLessButton.AddToClassList("asset-preview-description-hide-button"); + _showLessButton.AddToClassList("asset-preview-description-hyperlink-button"); + + _descriptionFullContainer.Add(validatorDescription); + _descriptionFullContainer.Add(_showLessButton); + + _descriptionFullContainer.style.display = DisplayStyle.None; + Add(_descriptionFullContainer); + } + + private void ToggleFullDescription() + { + var displayFullDescription = _descriptionFullContainer.style.display == DisplayStyle.None; + + if (displayFullDescription) + { + _showMoreButton.style.display = DisplayStyle.None; + _descriptionFullContainer.style.display = DisplayStyle.Flex; + } + else + { + _showMoreButton.style.display = DisplayStyle.Flex; + _descriptionFullContainer.style.display = DisplayStyle.None; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs.meta new file mode 100644 index 0000000..5f0631e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2cab289a87b0ba74f89cb458ff6d44f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Elements/PreviewWindowDescriptionElement.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs new file mode 100644 index 0000000..3a2d36f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs @@ -0,0 +1,51 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Services; +using AssetStoreTools.Previews.UI.Views; +using AssetStoreTools.Utility; +using UnityEditor.UIElements; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI +{ + internal class PreviewGeneratorWindow : AssetStoreToolsWindow + { + protected override string WindowTitle => "Preview Generator"; + + private ICachingService _cachingService; + + private PreviewListView _previewListView; + + protected override void Init() + { + minSize = new Vector2(350, 350); + + this.SetAntiAliasing(4); + + VisualElement root = rootVisualElement; + + // Getting a reference to the USS Document and adding stylesheet to the root + root.styleSheets.Add(StyleSelector.PreviewGeneratorWindow.PreviewGeneratorWindowStyle); + root.styleSheets.Add(StyleSelector.PreviewGeneratorWindow.PreviewGeneratorWindowTheme); + + GetServices(); + ConstructWindow(); + } + + private void GetServices() + { + _cachingService = PreviewServiceProvider.Instance.GetService(); + } + + private void ConstructWindow() + { + _previewListView = new PreviewListView(_cachingService); + rootVisualElement.Add(_previewListView); + } + + public void Load(PreviewGenerationSettings settings) + { + _previewListView.LoadSettings(settings); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs.meta new file mode 100644 index 0000000..04e4202 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4cad15de2de8cdc46b48a4b05eac5d78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/PreviewGeneratorWindow.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views.meta new file mode 100644 index 0000000..598279e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5e154861b0e2af64b93f6c831e6c0dc2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs new file mode 100644 index 0000000..36f5264 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs @@ -0,0 +1,142 @@ +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Services; +using AssetStoreTools.Previews.UI.Data; +using AssetStoreTools.Previews.UI.Elements; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Previews.UI.Views +{ + internal class PreviewListView : VisualElement + { + //Data + private PreviewDatabase _previewDatabase; + private IPreviewGeneratorSettings _previewGeneratorSettings; + private IAssetPreviewCollection _previewCollection; + + private ICachingService _cachingService; + + // UI + private PreviewWindowDescriptionElement _descriptionElement; + private PreviewGeneratorSettingsElement _settingsElement; + private PreviewGenerateButtonElement _generateButtonElement; + private PreviewCollectionElement _previewCollectionElement; + + public PreviewListView(ICachingService cachingService) + { + _cachingService = cachingService; + + _previewGeneratorSettings = new PreviewGeneratorSettings(); + _previewCollection = new AssetPreviewCollection(); + + _previewGeneratorSettings.OnGenerationTypeChanged += RefreshPreviewList; + _previewGeneratorSettings.OnGenerationPathsChanged += RefreshPreviewList; + + Create(); + RefreshPreviewList(); + } + + private void Create() + { + CreateDescription(); + CreateSettings(); + CreateGenerateButton(); + CreatePreviewList(); + } + + private void CreateDescription() + { + _descriptionElement = new PreviewWindowDescriptionElement(); + Add(_descriptionElement); + } + + private void CreateSettings() + { + _settingsElement = new PreviewGeneratorSettingsElement(_previewGeneratorSettings); + Add(_settingsElement); + } + + private void CreateGenerateButton() + { + _generateButtonElement = new PreviewGenerateButtonElement(_previewGeneratorSettings); + _generateButtonElement.OnGenerate += GeneratePreviews; + Add(_generateButtonElement); + } + + private void CreatePreviewList() + { + _previewCollectionElement = new PreviewCollectionElement(_previewCollection); + Add(_previewCollectionElement); + } + + private async void GeneratePreviews() + { + try + { + _settingsElement.SetEnabled(false); + _generateButtonElement.SetEnabled(false); + _previewCollectionElement.SetEnabled(false); + + var generator = _previewGeneratorSettings.CreateGenerator(); + generator.OnProgressChanged += DisplayProgress; + var result = await generator.Generate(); + generator.OnProgressChanged -= DisplayProgress; + + if (!result.Success) + { + EditorUtility.DisplayDialog("Error", result.Exception.Message, "OK"); + Debug.LogException(result.Exception); + return; + } + + RefreshPreviewList(); + } + finally + { + _settingsElement.SetEnabled(true); + _generateButtonElement.SetEnabled(true); + _previewCollectionElement.SetEnabled(true); + EditorUtility.ClearProgressBar(); + } + } + + private void DisplayProgress(float progress) + { + EditorUtility.DisplayProgressBar("Generating", "Generating previews...", progress); + } + + public void LoadSettings(PreviewGenerationSettings settings) + { + _previewGeneratorSettings.LoadSettings(settings); + } + + private void RefreshPreviewList() + { + if (!_cachingService.GetCachedMetadata(out _previewDatabase)) + _previewDatabase = new PreviewDatabase(); + + var paths = _previewGeneratorSettings.GetGenerationPaths(); + var guids = AssetDatabase.FindAssets("", paths.ToArray()); + var displayedPreviews = new List(); + + foreach (var entry in _previewDatabase.Previews) + { + if (!entry.Exists()) + continue; + + if (entry.Type != _previewGeneratorSettings.GetGenerationType()) + continue; + + if (!guids.Any(x => x == entry.Guid)) + continue; + + displayedPreviews.Add(entry); + } + + _previewCollection.Refresh(_previewGeneratorSettings.GetGenerationType(), displayedPreviews); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs.meta new file mode 100644 index 0000000..5c2a716 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 94d417240bb510d469acb8a11f15b277 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/UI/Views/PreviewListView.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility.meta similarity index 77% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility.meta rename to Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility.meta index 05c216e..92fdd4f 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 342ae2f53a9d6714ca92eacbecb27801 +guid: 99cf24252c136f246bfa4b02a69fe992 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs new file mode 100644 index 0000000..1a304f0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs @@ -0,0 +1,96 @@ +using UnityEngine; + +namespace AssetStoreTools.Previews.Utility +{ + internal static class GraphicsUtility + { + public static Texture2D GetTextureFromCamera(Camera camera, int desiredWidth, int desiredHeight, int desiredDepth) + { + var texture = new Texture2D(desiredWidth, desiredHeight); + var originalRenderTexture = RenderTexture.active; + var renderTexture = RenderTexture.GetTemporary(desiredWidth, desiredHeight, desiredDepth); + var cameraInitiallyEnabled = camera.enabled; + + try + { + if (cameraInitiallyEnabled) + camera.enabled = false; + + camera.targetTexture = renderTexture; + camera.Render(); + + RenderTexture.active = renderTexture; + texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); + texture.Apply(); + } + finally + { + camera.targetTexture = null; + RenderTexture.active = originalRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + camera.enabled = cameraInitiallyEnabled; + } + + return texture; + } + + public static Texture2D ResizeTexture(Texture2D source, int desiredWidth, int desiredHeight) + { + var texture = new Texture2D(desiredWidth, desiredHeight); + var originalRenderTexture = RenderTexture.active; + var renderTexture = RenderTexture.GetTemporary(desiredWidth, desiredHeight, 32); + + try + { + RenderTexture.active = renderTexture; + Graphics.Blit(source, renderTexture); + + texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + texture.Apply(); + } + finally + { + RenderTexture.active = originalRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + } + + return texture; + } + + public static Texture2D ResizeTextureNormalMap(Texture2D source, int desiredWidth, int desiredHeight) + { + var texture = new Texture2D(desiredWidth, desiredHeight); + var originalRenderTexture = RenderTexture.active; + var renderTexture = RenderTexture.GetTemporary(desiredWidth, desiredHeight, 32, RenderTextureFormat.Default, RenderTextureReadWrite.Linear); + + try + { + RenderTexture.active = renderTexture; + Graphics.Blit(source, renderTexture); + + texture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + + for (int i = 0; i < texture.width; i++) + { + for (int j = 0; j < texture.height; j++) + { + var color = texture.GetPixel(i, j); + color.b = color.r; + color.r = color.a; + color.a = 1; + texture.SetPixel(i, j, color); + } + } + + texture.Apply(); + } + finally + { + RenderTexture.active = originalRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + } + + return texture; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs.meta new file mode 100644 index 0000000..9acc1c2 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f0a4fc8f266b4dd41a59693dd581e232 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/GraphicsUtility.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs new file mode 100644 index 0000000..ab93f97 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs @@ -0,0 +1,72 @@ +using AssetStoreTools.Previews.Data; +using UnityEditor; +using UnityEngine; + +namespace AssetStoreTools.Previews.Utility +{ + internal static class PreviewConvertUtility + { + public static string ConvertFilename(Object asset, FileNameFormat format) + { + string fileName = string.Empty; + + switch (format) + { + case FileNameFormat.Guid: + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long _); + fileName = guid; + break; + case FileNameFormat.FullAssetPath: + var assetPath = AssetDatabase.GetAssetPath(asset); + + if (assetPath.StartsWith("Assets/")) + fileName = assetPath.Substring("Assets/".Length); + else if (assetPath.StartsWith("Packages/")) + fileName = assetPath.Substring("Packages/".Length); + + fileName = fileName.Replace("/", "_"); + break; + case FileNameFormat.AssetName: + fileName = asset.name; + break; + default: + throw new System.Exception("Undefined format"); + } + + return fileName; + } + + public static string ConvertExtension(PreviewFormat format) + { + switch (format) + { + case PreviewFormat.JPG: + return "jpg"; + case PreviewFormat.PNG: + return "png"; + default: + throw new System.Exception("Undefined format"); + } + } + + public static string ConvertFilenameWithExtension(Object asset, FileNameFormat nameFormat, PreviewFormat imageFormat) + { + var filename = ConvertFilename(asset, nameFormat); + var extension = ConvertExtension(imageFormat); + return $"{filename}.{extension}"; + } + + public static byte[] ConvertTexture(Texture2D texture, PreviewFormat format) + { + switch (format) + { + case PreviewFormat.JPG: + return texture.EncodeToJPG(); + case PreviewFormat.PNG: + return texture.EncodeToPNG(); + default: + throw new System.Exception("Undefined format"); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs.meta new file mode 100644 index 0000000..e9b337d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 700eaf82299628d44853599774664bea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewConvertUtility.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs new file mode 100644 index 0000000..8ca2b3a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs @@ -0,0 +1,196 @@ +using System; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +#if AST_URP_AVAILABLE +using UnityEngine.Rendering.Universal; +#endif +#if AST_HDRP_AVAILABLE +using UnityEngine.Rendering; +using UnityEngine.Rendering.HighDefinition; +#endif + +namespace AssetStoreTools.Previews.Utility +{ + internal static class PreviewSceneUtility + { + private const string PreviewSceneName = "Preview Generation In Progress"; + private static readonly Color BackgroundColor = new Color(82f / 255, 82f / 255, 82f / 255); + private static readonly Color BackgroundColorHDRP = new Color(38f / 255, 38f / 255, 38f / 255); + + public static async Task OpenPreviewSceneForCurrentPipeline() + { + // Wait for an Editor frame to avoid recursive player loop internal errors + await WaitForEditorUpdate(); + + switch (RenderPipelineUtility.GetCurrentPipeline()) + { + case RenderPipeline.BiRP: + await OpenPreviewSceneBiRP(); + break; +#if AST_URP_AVAILABLE + case RenderPipeline.URP: + await OpenPreviewSceneURP(); + break; +#endif +#if AST_HDRP_AVAILABLE + case RenderPipeline.HDRP: + await OpenPreviewSceneHDRP(); + break; +#endif + default: + throw new NotImplementedException("Undefined Render Pipeline"); + } + } + + private static async Task WaitForEditorUpdate() + { + var updateCalled = false; + var delayCalled = false; + + void Update() + { + EditorApplication.update -= Update; + updateCalled = true; + } + + EditorApplication.update += Update; + while (!updateCalled) + await Task.Delay(10); + + void DelayCall() + { + EditorApplication.delayCall -= DelayCall; + delayCalled = true; + } + + EditorApplication.delayCall += DelayCall; + while (!delayCalled) + await Task.Delay(10); + } + + public static async Task OpenPreviewSceneBiRP() + { + OpenNewScene(); + + CreateSceneCamera(); + CreateSceneLighting(); + + await WaitForLighting(); + } + + private static void OpenNewScene() + { + EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); + var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + scene.name = PreviewSceneName; + } + + private static Camera CreateSceneCamera() + { + var cameraGO = new GameObject() { name = "Camera" }; + var camera = cameraGO.AddComponent(); + camera.enabled = false; + camera.tag = "MainCamera"; + + camera.nearClipPlane = 0.01f; + camera.farClipPlane = 100000; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = BackgroundColor; + + return camera; + } + + private static Light CreateSceneLighting() + { + var lightGO = new GameObject() { name = "Lights" }; + lightGO.transform.rotation = Quaternion.Euler(45, 225, 0); + var light = lightGO.AddComponent(); + light.intensity = 0.75f; + light.type = LightType.Directional; + light.shadows = LightShadows.None; + + return light; + } + + private static async Task WaitForLighting() + { + while (!DynamicGI.isConverged) + await Task.Delay(100); + + await Task.Yield(); + } + +#if AST_URP_AVAILABLE + public static async Task OpenPreviewSceneURP() + { + OpenNewScene(); + + var camera = CreateSceneCamera(); + camera.gameObject.AddComponent(); + + var lighting = CreateSceneLighting(); + lighting.intensity = 0.5f; + lighting.gameObject.AddComponent(); + + await WaitForLighting(); + } +#endif + +#if AST_HDRP_AVAILABLE + public static async Task OpenPreviewSceneHDRP() + { + OpenNewScene(); + + var camera = CreateSceneCamera(); + var cameraData = camera.gameObject.AddComponent(); + cameraData.clearColorMode = HDAdditionalCameraData.ClearColorMode.Color; + cameraData.backgroundColorHDR = BackgroundColorHDRP; + + var light = CreateSceneLighting(); + var lightData = light.gameObject.AddComponent(); + lightData.SetIntensity(5000, LightUnit.Lux); + + CreateHDRPVolumeProfile(); + + await WaitForLighting(); + } + + private static Volume CreateHDRPVolumeProfile() + { + var volumeGO = new GameObject() { name = "Volume" }; + var volume = volumeGO.gameObject.AddComponent(); + + var profile = VolumeProfile.CreateInstance(); + volume.profile = profile; + volume.isGlobal = true; + + var exposure = profile.Add(); + exposure.active = true; + + exposure.mode.overrideState = true; + exposure.mode.value = ExposureMode.Fixed; + + exposure.fixedExposure.overrideState = true; + exposure.fixedExposure.value = 11; + + var fog = profile.Add(); + fog.active = true; + + fog.enabled.overrideState = true; + fog.enabled.value = false; + +#if AST_HDRP_AVAILABLE_V12 + var volumetricClouds = profile.Add(); + volumetricClouds.active = true; + + volumetricClouds.enable.overrideState = true; + volumetricClouds.enable.value = false; +#endif + + return volume; + } +#endif + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs.meta new file mode 100644 index 0000000..c11d3fb --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 63fa5650920e7914dae6fe76badac249 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/PreviewSceneUtility.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs new file mode 100644 index 0000000..028f96d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs @@ -0,0 +1,10 @@ +namespace AssetStoreTools.Previews.Utility +{ + internal enum RenderPipeline + { + Unknown = 0, + BiRP = 1, + URP = 2, + HDRP = 3 + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs.meta new file mode 100644 index 0000000..cbc5929 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c43c7ce2b9090ab49bb8944bc6bdb3c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipeline.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs new file mode 100644 index 0000000..c8e79d5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs @@ -0,0 +1,32 @@ +using UnityEngine.Rendering; +#if AST_URP_AVAILABLE +using UnityEngine.Rendering.Universal; +#endif +#if AST_HDRP_AVAILABLE +using UnityEngine.Rendering.HighDefinition; +#endif + +namespace AssetStoreTools.Previews.Utility +{ + internal static class RenderPipelineUtility + { + public static RenderPipeline GetCurrentPipeline() + { + var currentPipelineAsset = GraphicsSettings.currentRenderPipeline; + if (currentPipelineAsset == null) + return RenderPipeline.BiRP; + +#if AST_URP_AVAILABLE + if (currentPipelineAsset is UniversalRenderPipelineAsset) + return RenderPipeline.URP; +#endif + +#if AST_HDRP_AVAILABLE + if (currentPipelineAsset is HDRenderPipelineAsset) + return RenderPipeline.HDRP; +#endif + + return RenderPipeline.Unknown; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs.meta new file mode 100644 index 0000000..96956b5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5e42bdf53cd8b51429b10a6742ec5272 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Utility/RenderPipelineUtility.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles.meta new file mode 100644 index 0000000..50a0018 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 70d30555bce30014a9143c3d003105bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss new file mode 100644 index 0000000..c363a3d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss @@ -0,0 +1,210 @@ +/* Asset Preview Description */ + +.asset-preview-description { + flex-direction: column; + flex-shrink: 0; + + margin: 10px 5px 2px 5px; + padding: 2px 4px; +} + +.asset-preview-description-simple-container { + flex-direction: column; + flex-wrap: wrap; +} + +.asset-preview-description-simple-label { + white-space: normal; +} + +.asset-preview-description-hyperlink-button { + margin: 0; + padding: 0; + + align-self: flex-start; + cursor: link; +} + +.asset-preview-description-show-button { + margin-top: 12px; +} + +.asset-preview-description-hide-button { + margin-top: 12px; +} + +.asset-preview-description-full-container { + margin-top: 12px; +} + +.asset-preview-description-full-label { + white-space: normal; +} + +/* Asset Preview Settings */ + +.preview-settings { + flex-direction: column; + flex-shrink: 0; + + margin: 0px 5px 2px 5px; + padding: 2px 4px; +} + +.preview-settings-selection-row { + flex-direction: row; + flex-grow: 1; + + margin-top: 10px; + padding: 0 3px 0 2px; +} + +.preview-settings-selection-label-help-row { + flex-direction: row; + flex-shrink: 1; + flex-grow: 0; + + align-self: center; + align-items: center; + justify-content: flex-start; + + width: 120px; +} + +.preview-settings-selection-label-help-row > Label { + -unity-font-style: bold; +} + +.preview-settings-selection-label-help-row > Image { + height: 16px; + width: 16px; +} + +.preview-settings-selection-dropdown { + flex-grow: 1; + flex-shrink: 1; + + align-self: stretch; + + margin-right: 0; + margin-left: 3px; + padding: 1px 4px; +} + +/* Preview Paths */ + +.preview-paths { + flex-direction: column; + flex-grow: 1; + flex-shrink: 0; + + margin-bottom: 10px; + padding: 0; +} + +.preview-paths-box { + flex-grow: 1; + flex-direction: column; +} + +.preview-paths-scroll-view { + flex-grow: 1; + height: 100px; + margin-left: 3px; +} + +.preview-paths-scroll-view > .unity-scroll-view__content-viewport +{ + margin-left: 1px; +} + +.preview-paths-scroll-view > * > .unity-scroll-view__content-container +{ + padding: 0 0 0 0; +} + +.preview-paths-scroll-view > * > .unity-scroll-view__vertical-scroller +{ + margin: -1px 0; +} + +.preview-paths-scroll-view-bottom-row { + flex-direction: row-reverse; + margin: -1px 0 0 3px; +} + +.preview-paths-add-button { + margin: 3px 0 0 0; + align-self: center; +} + +.preview-paths-path-row { + flex-direction: row; + flex-grow: 1; + + margin-top: 2px; + padding: 0 5px 0 2px; +} + +.preview-paths-path-row-input-field { + flex-grow: 1; + flex-shrink: 1; + + padding-left: 5px; + + white-space: normal; + -unity-text-align: middle-left; +} + +.preview-paths-path-row-remove-button { + width: 20px; + height: 20px; + margin-left: 2px; + margin-right: 1px; + padding: 1px 0 0 0; +} + +/* Generate Button */ + +.preview-generate-button { + align-self: stretch; + + height: 25px; + margin-left: 2px; +} + +/* Asset Preview List Element */ + +.preview-list { + margin-top: 10px; + flex-grow: 1; +} + +.preview-list-image { + width: 140px; + height: 160px; + margin: 5px; + padding: 5px; + justify-content: space-between; +} + +.preview-list-image:hover{ + background-color: #444444; +} + +.preview-list-image > Image { + flex-shrink: 0; + max-width: 100%; + max-height: 100%; + -unity-background-scale-mode: scale-to-fit; + align-self: center; +} + +.preview-list-image > Label { + align-self: center; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 2px; + padding: 0; + -unity-text-align: middle-center; +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss.meta new file mode 100644 index 0000000..14dfd5d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 095b74bd60b187c418dcc4cd47aa696d +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Styles/Style.uss + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss new file mode 100644 index 0000000..6dff0dd --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss @@ -0,0 +1,67 @@ +.primary-colors { + /* Light - lighter */ + background-color: rgb(220, 220, 220); + /* Light - middle */ + background-color: rgb(200, 200, 200); + /* Light - darker */ + background-color: rgb(180, 180, 180); + + /* Dark - lighter */ + background-color: rgb(78, 78, 78); + /* Dark - middle */ + background-color: rgb(68, 68, 68); + /* Dark - darker */ + background-color: rgb(58, 58, 58); + + /* Border color - light */ + border-color: rgb(200, 200, 200); + /* Border color - dark */ + border-color: rgb(33, 33, 33); +} + +/* Asset Preview Description */ + +.asset-preview-description-hyperlink-button { + color: rgb(68, 113, 229); + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.asset-preview-description-hyperlink-button:hover { + color: rgb(68, 133, 229); +} + +.asset-preview-description-hyperlink-button:active { + color: rgb(68, 93, 229); +} + +/* Asset Preview Settings */ + +.preview-settings-selection-label-help-row > Image { + --unity-image: resource("d__Help@2x"); +} + +.preview-settings-selection-dropdown { + color: rgb(238, 238, 238); + background-color: rgb(88, 88, 88); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(36, 36, 36); +} + +/* Preview Paths */ + +.preview-paths-scroll-view { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(58, 58, 58); +} + +.preview-paths-scroll-view > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.preview-paths-path-row-input-field:hover { + background-color: rgb(78, 78, 78); +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss.meta new file mode 100644 index 0000000..8295d42 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1c04ee69303d45644bb3971a4e8ce952 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeDark.uss + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss new file mode 100644 index 0000000..b98e1cd --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss @@ -0,0 +1,67 @@ +.primary-colors { + /* Light - lighter */ + background-color: rgb(220, 220, 220); + /* Light - middle */ + background-color: rgb(200, 200, 200); + /* Light - darker */ + background-color: rgb(180, 180, 180); + + /* Dark - lighter */ + background-color: rgb(50, 50, 50); + /* Dark - middle */ + background-color: rgb(28, 28, 28); + /* Dark - darker */ + background-color: rgb(0, 0, 0); + + /* Border color - light */ + border-color: rgb(200, 200, 200); + /* Border color - dark */ + border-color: rgb(33, 33, 33); +} + +/* Asset Preview Description */ + +.asset-preview-description-hyperlink-button { + color: rgb(68, 113, 229); + border-width: 0; + background-color: rgba(0, 0, 0, 0); +} + +.asset-preview-description-hyperlink-button:hover { + color: rgb(68, 133, 229); +} + +.asset-preview-description-hyperlink-button:active { + color: rgb(68, 93, 229); +} + +/* Asset Preview Settings */ + +.preview-settings-selection-label-help-row > Image { + --unity-image: resource("_Help@2x"); +} + +.preview-settings-selection-dropdown { + color: rgb(9, 9, 9); + background-color: rgb(228, 228, 228); + + border-width: 1px; + border-radius: 3px; + border-color: rgb(178, 178, 178); +} + +/* Preview Paths */ + +.preview-paths-scroll-view { + border-width: 1px; + border-color: rgb(33, 33, 33); + background-color: rgb(180, 180, 180); +} + +.preview-paths-scroll-view > * > .unity-scroll-view__vertical-scroller { + border-right-width: 0; +} + +.preview-paths-path-row-input-field:hover { + background-color: rgb(200, 200, 200); +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss.meta b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss.meta new file mode 100644 index 0000000..f522d08 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 38ae9e6ef965cae43902ba22967938ee +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Styles/ThemeLight.uss + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef b/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef index eb298a8..b0ba405 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef +++ b/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef @@ -1,6 +1,11 @@ { "name": "asset-store-tools-editor", - "references": [], + "rootNamespace": "", + "references": [ + "Unity.RenderPipelines.Universal.Runtime", + "Unity.RenderPipelines.Core.Runtime", + "Unity.RenderPipelines.HighDefinition.Runtime" + ], "includePlatforms": [ "Editor" ], @@ -10,6 +15,22 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.render-pipelines.universal", + "expression": "1.0.0", + "define": "AST_URP_AVAILABLE" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "1.0.0", + "define": "AST_HDRP_AVAILABLE" + }, + { + "name": "com.unity.render-pipelines.high-definition", + "expression": "12.0.0", + "define": "AST_HDRP_AVAILABLE_V12" + } + ], "noEngineReferences": false } \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta b/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta index 61ada0f..5e13508 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef.meta @@ -9,6 +9,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 + packageVersion: 12.0.1 assetPath: Packages/com.unity.asset-store-tools/Editor/Unity.AssetStoreTools.Editor.asmdef - uploadId: 712972 + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs deleted file mode 100644 index d8b0c4b..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/AssetStoreUploader.cs +++ /dev/null @@ -1,227 +0,0 @@ -using AssetStoreTools.Utility.Json; -using System; -using System.Collections.Generic; -using AssetStoreTools.Utility; -using UnityEditor; -using UnityEditor.UIElements; -using UnityEngine; -using UnityEngine.UIElements; -using AssetStoreTools.Uploader.Utility; -using AssetStoreTools.Uploader.UIElements; - -namespace AssetStoreTools.Uploader -{ - internal class AssetStoreUploader : AssetStoreToolsWindow - { - public const string MinRequiredPackageVersion = "2021.3"; - public const long MaxPackageSizeBytes = 6442450944; // 6 GB - - private const string MainWindowVisualTree = "Packages/com.unity.asset-store-tools/Editor/Uploader/Styles/Base/BaseWindow_Main"; - private const string DebugPhrase = "debug"; - - // UI Windows - private LoginWindow _loginWindow; - private UploadWindow _uploadWindow; - - private readonly List _debugBuffer = new List(); - - public static bool ShowPackageVersionDialog - { - get => string.Compare(Application.unityVersion, MinRequiredPackageVersion, StringComparison.Ordinal) == -1 && ASToolsPreferences.Instance.UploadVersionCheck; - set { ASToolsPreferences.Instance.UploadVersionCheck = value; ASToolsPreferences.Instance.Save(); } - } - - protected override string WindowTitle => "Asset Store Uploader"; - - protected override void Init() - { - if (_loginWindow != null && _uploadWindow != null) - return; - - minSize = new Vector2(400, 430); - this.SetAntiAliasing(4); - - base.Init(); - - VisualElement root = rootVisualElement; - root.AddToClassList("root"); - - // Getting a reference to the UXML Document and adding to the root - var visualTree = AssetDatabase.LoadAssetAtPath($"{MainWindowVisualTree}.uxml"); - VisualElement uxmlRoot = visualTree.CloneTree(); - uxmlRoot.style.flexGrow = 1; - root.Add(uxmlRoot); - - root.styleSheets.Add(StyleSelector.UploaderWindow.BaseWindowStyle); - root.styleSheets.Add(StyleSelector.UploaderWindow.BaseWindowTheme); - - - // Find necessary windows / views and sets up appropriate functionality - SetupCoreElements(); - - if (!AssetStoreAPI.IsUploading) - { - // Should only authenticate if the session is available. Other authentications are only available - // in the login window. See "SetupLoginElements". - HideElement(_uploadWindow); - Authenticate(); - } - else - { - ShowUploadWindow(); - } - } - - private void OnGUI() - { - CheckForDebugMode(); - } - - private void OnDestroy() - { - if (AssetStoreAPI.IsUploading) - EditorUtility.DisplayDialog("Notice", "Assets are still being uploaded to the Asset Store. " + - "If you wish to check on the progress, please re-open the Asset Store Uploader window", "OK"); - } - - private void SetupCoreElements() - { - _loginWindow = rootVisualElement.Q("LoginWindow"); - _uploadWindow = rootVisualElement.Q("UploadWindow"); - - _loginWindow.SetupLoginElements(OnLoginSuccess, OnLoginFail); - _uploadWindow.SetupWindows(OnLogout, OnPackageDownloadFail); - } - - #region Login Interface - - private async void Authenticate() - { - ShowLoginWindow(); - - // 1 - Check if there's an active session - // 2 - Check if there's a saved session - // 3 - Attempt to login via Cloud session token - // 4 - Prompt manual login - EnableLoginWindow(false); - var result = await AssetStoreAPI.LoginWithSessionAsync(); - if (result.Success) - OnLoginSuccess(result.Response); - else if (result.SilentFail) - OnLoginFailSession(); - else - OnLoginFail(result.Error); - } - - private void OnLoginFail(ASError error) - { - Debug.LogError(error.Message); - - _loginWindow.EnableErrorBox(true, error.Message); - EnableLoginWindow(true); - } - - private void OnLoginFailSession() - { - // All previous login methods are unavailable - EnableLoginWindow(true); - } - - private void OnLoginSuccess(JsonValue json) - { - ASDebug.Log($"Login json\n{json}"); - - if (!AssetStoreAPI.IsPublisherValid(json, out var error)) - { - EnableLoginWindow(true); - _loginWindow.EnableErrorBox(true, error.Message); - ASDebug.Log($"Publisher {json["name"]} is invalid."); - return; - } - - ASDebug.Log($"Publisher {json["name"]} is valid."); - AssetStoreAPI.SavedSessionId = json["xunitysession"].AsString(); - AssetStoreAPI.LastLoggedInUser = json["username"].AsString(); - - ShowUploadWindow(); - } - - private void OnPackageDownloadFail(ASError error) - { - _loginWindow.EnableErrorBox(true, error.Message); - EnableLoginWindow(true); - ShowLoginWindow(); - } - - private void OnLogout() - { - AssetStoreAPI.SavedSessionId = String.Empty; - AssetStoreCache.ClearTempCache(); - - _loginWindow.ClearLoginBoxes(); - ShowLoginWindow(); - EnableLoginWindow(true); - } - - #endregion - - #region UI Window Utils - private void ShowLoginWindow() - { - HideElement(_uploadWindow); - ShowElement(_loginWindow); - } - - private void ShowUploadWindow() - { - HideElement(_loginWindow); - ShowElement(_uploadWindow); - - _uploadWindow.ShowAllPackagesView(); - _uploadWindow.ShowPublisherEmail(AssetStoreAPI.LastLoggedInUser); - _uploadWindow.LoadPackages(true, OnPackageDownloadFail); - } - - private void ShowElement(params VisualElement[] elements) - { - foreach(var e in elements) - e.style.display = DisplayStyle.Flex; - } - - private void HideElement(params VisualElement[] elements) - { - foreach(var e in elements) - e.style.display = DisplayStyle.None; - } - - private void EnableLoginWindow(bool enable) - { - _loginWindow.SetEnabled(enable); - } - - #endregion - - #region Debug Utility - - private void CheckForDebugMode() - { - Event e = Event.current; - - if (e.type != EventType.KeyDown || e.keyCode == KeyCode.None) - return; - - _debugBuffer.Add(e.keyCode.ToString().ToLower()[0]); - if (_debugBuffer.Count > DebugPhrase.Length) - _debugBuffer.RemoveAt(0); - - if (string.Join(string.Empty, _debugBuffer.ToArray()) != DebugPhrase) - return; - - ASDebug.DebugModeEnabled = !ASDebug.DebugModeEnabled; - ASDebug.Log($"DEBUG MODE ENABLED: {ASDebug.DebugModeEnabled}"); - _debugBuffer.Clear(); - } - - #endregion - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png new file mode 100644 index 0000000..f54f1a2 Binary files /dev/null and b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png differ diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png.meta new file mode 100644 index 0000000..f4bf516 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png.meta @@ -0,0 +1,130 @@ +fileFormatVersion: 2 +guid: 92f8a779a7c786a4f87ed8e1b36a66b3 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-dark.png + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png new file mode 100644 index 0000000..d6684e5 Binary files /dev/null and b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png differ diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png.meta new file mode 100644 index 0000000..769b5ba --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png.meta @@ -0,0 +1,130 @@ +fileFormatVersion: 2 +guid: 7c0661b9a6385a3488c075711f368cf4 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/account-light.png + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta index 5cb49d0..d0b3e91 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png.meta @@ -149,6 +149,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 + packageVersion: 12.0.1 assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/open-in-browser.png - uploadId: 712972 + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png similarity index 100% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png.meta similarity index 97% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png.meta rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png.meta index 1ebdfaf..7676a31 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png.meta @@ -130,6 +130,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_white.png - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-dark.png + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png similarity index 100% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png.meta similarity index 90% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png.meta rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png.meta index c4659f8..7162554 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png.meta @@ -3,7 +3,7 @@ guid: 8e0749dce5b14cc46b73b0303375c162 TextureImporter: internalIDToNameTable: [] externalObjects: {} - serializedVersion: 11 + serializedVersion: 12 mipmaps: mipMapMode: 0 enableMipMap: 1 @@ -23,6 +23,8 @@ TextureImporter: isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 @@ -40,7 +42,7 @@ TextureImporter: nPOTScale: 0 lightmap: 0 compressionQuality: 50 - spriteMode: 2 + spriteMode: 1 spriteExtrude: 1 spriteMeshType: 1 alignment: 0 @@ -54,10 +56,14 @@ TextureImporter: textureType: 8 textureShape: 1 singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 + ignorePngGamma: 0 applyGammaDecoding: 0 + cookieLightType: 1 platformSettings: - serializedVersion: 3 buildTarget: DefaultTexturePlatform @@ -113,13 +119,14 @@ TextureImporter: outline: [] physicsShape: [] bones: [] - spriteID: + spriteID: 5e97eb03825dee720800000000000000 internalID: 0 vertices: [] indices: edges: [] weights: [] secondaryTextures: [] + nameFileIdTable: {} spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 @@ -130,6 +137,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher_portal_black.png - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Icons/publisher-portal-light.png + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs deleted file mode 100644 index ad50a60..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs +++ /dev/null @@ -1,796 +0,0 @@ -using AssetStoreTools.Uploader.Data; -using AssetStoreTools.Uploader.Utility; -using AssetStoreTools.Utility; -using AssetStoreTools.Utility.Json; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using UnityEditor; -using UnityEngine; - -namespace AssetStoreTools.Uploader -{ - /// - /// A class for retrieving data from the Asset Store backend - /// Note: most data retrieval methods require to be set - /// - internal static class AssetStoreAPI - { - public const string ToolVersion = "V11.4.4"; - - private const string UnauthSessionId = "26c4202eb475d02864b40827dfff11a14657aa41"; - private const string KharmaSessionId = "kharma.sessionid"; - private const int UploadResponseTimeoutMs = 10000; - - public static string AssetStoreProdUrl = "https://kharma.unity3d.com"; - private static string s_sessionId = EditorPrefs.GetString(KharmaSessionId); - private static HttpClient httpClient = new HttpClient(); - private static CancellationTokenSource s_downloadCancellationSource; - - public static string SavedSessionId - { - get => s_sessionId; - set - { - s_sessionId = value; - EditorPrefs.SetString(KharmaSessionId, value); - httpClient.DefaultRequestHeaders.Clear(); - if (!string.IsNullOrEmpty(value)) - httpClient.DefaultRequestHeaders.Add("X-Unity-Session", SavedSessionId); - } - } - - public static bool IsCloudUserAvailable => CloudProjectSettings.userName != "anonymous"; - public static string LastLoggedInUser = ""; - public static ConcurrentDictionary ActiveUploads = new ConcurrentDictionary(); - public static bool IsUploading => (ActiveUploads.Count > 0); - - static AssetStoreAPI() - { - ServicePointManager.DefaultConnectionLimit = 500; - httpClient.DefaultRequestHeaders.ConnectionClose = false; - httpClient.Timeout = TimeSpan.FromMinutes(1320); - } - - /// - /// A structure used to return the success outcome and the result of Asset Store API calls - /// - internal class APIResult - { - public JsonValue Response; - public bool Success; - public bool SilentFail; - public ASError Error; - - public static implicit operator bool(APIResult value) - { - return value != null && value.Success != false; - } - } - - #region Login API - - /// - /// A login API call that uses the email and password credentials - /// - /// - /// Note: this method only returns a response from the server and does not set the itself - /// - public static async Task LoginWithCredentialsAsync(string email, string password) - { - FormUrlEncodedContent data = GetLoginContent(new Dictionary { { "user", email }, { "pass", password } }); - return await LoginAsync(data); - } - - /// - /// A login API call that uses the - /// - /// - /// Note: this method only returns a response from the server and does not set the itself - /// - public static async Task LoginWithSessionAsync() - { - if (string.IsNullOrEmpty(SavedSessionId)) - return new APIResult() { Success = false, SilentFail = true, Error = ASError.GetGenericError(new Exception("No active session available")) }; - - FormUrlEncodedContent data = GetLoginContent(new Dictionary { { "reuse_session", SavedSessionId }, { "xunitysession", UnauthSessionId } }); - return await LoginAsync(data); - } - - /// - /// A login API call that uses the - /// - /// - /// Note: this method only returns a response from the server and does not set the itself - /// - /// Cloud access token. Can be retrieved by calling - public static async Task LoginWithTokenAsync(string token) - { - FormUrlEncodedContent data = GetLoginContent(new Dictionary { { "user_access_token", token } }); - return await LoginAsync(data); - } - - private static async Task LoginAsync(FormUrlEncodedContent data) - { - OverrideAssetStoreUrl(); - Uri uri = new Uri($"{AssetStoreProdUrl}/login"); - - httpClient.DefaultRequestHeaders.Clear(); - httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); - - try - { - var response = await httpClient.PostAsync(uri, data); - return UploadValuesCompletedLogin(response); - } - catch (Exception e) - { - return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; - } - } - - private static APIResult UploadValuesCompletedLogin(HttpResponseMessage response) - { - ASDebug.Log($"Upload Values Complete {response.ReasonPhrase}"); - ASDebug.Log($"Login success? {response.IsSuccessStatusCode}"); - try - { - response.EnsureSuccessStatusCode(); - var responseResult = response.Content.ReadAsStringAsync().Result; - var success = JSONParser.AssetStoreResponseParse(responseResult, out ASError error, out JsonValue jsonResult); - if (success) - return new APIResult() { Success = true, Response = jsonResult }; - else - return new APIResult() { Success = false, Error = error }; - } - catch (HttpRequestException ex) - { - return new APIResult() { Success = false, Error = ASError.GetLoginError(response, ex) }; - } - } - - #endregion - - #region Package Metadata API - - private static async Task GetPackageDataMain() - { - return await GetAssetStoreData(APIUri("asset-store-tools", "metadata/0", SavedSessionId)); - } - - private static async Task GetPackageDataExtra() - { - return await GetAssetStoreData(APIUri("management", "packages", SavedSessionId)); - } - - private static async Task GetCategories(bool useCached) - { - if (useCached) - { - if (AssetStoreCache.GetCachedCategories(out JsonValue cachedCategoryJson)) - return cachedCategoryJson; - - ASDebug.LogWarning("Failed to retrieve cached category data. Proceeding to download"); - } - var categoryJson = await GetAssetStoreData(APIUri("management", "categories", SavedSessionId)); - AssetStoreCache.CacheCategories(categoryJson); - - return categoryJson; - } - - /// - /// Retrieve data for all packages associated with the currently logged in account (identified by ) - /// - /// - /// - public static async Task GetFullPackageDataAsync(bool useCached) - { - if (useCached) - { - if (AssetStoreCache.GetCachedPackageMetadata(out JsonValue cachedData)) - return new APIResult() { Success = true, Response = cachedData }; - - ASDebug.LogWarning("Failed to retrieve cached package metadata. Proceeding to download"); - } - - try - { - var jsonMainData = await GetPackageDataMain(); - var jsonExtraData = await GetPackageDataExtra(); - var jsonCategoryData = await GetCategories(useCached); - - var joinedData = MergePackageData(jsonMainData, jsonExtraData, jsonCategoryData); - AssetStoreCache.CachePackageMetadata(joinedData); - - return new APIResult() { Success = true, Response = joinedData }; - } - catch (OperationCanceledException e) - { - ASDebug.Log("Package metadata download operation cancelled"); - DisposeDownloadCancellation(); - return new APIResult() { Success = false, SilentFail = true, Error = ASError.GetGenericError(e) }; - } - catch (Exception e) - { - return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; - } - } - - /// - /// Retrieve the thumbnail textures for all packages within the provided json structure and perform a given action after each retrieval - /// - /// A json file retrieved from - /// Return cached thumbnails if they are found - /// - /// Action to perform upon a successful thumbnail retrieval - /// - Package Id
- /// - Associated Thumbnail - /// - /// - /// Action to perform upon a failed thumbnail retrieval - /// - Package Id
- /// - Associated error - /// - public static async void GetPackageThumbnails(JsonValue packageJson, bool useCached, Action onSuccess, Action onFail) - { - SetupDownloadCancellation(); - var packageDict = packageJson["packages"].AsDict(); - var packageEnum = packageDict.GetEnumerator(); - - for (int i = 0; i < packageDict.Count; i++) - { - packageEnum.MoveNext(); - var package = packageEnum.Current; - - try - { - s_downloadCancellationSource.Token.ThrowIfCancellationRequested(); - - if (package.Value["icon_url"] - .IsNull()) // If no URL is found in the package metadata, use the default image - { - Texture2D fallbackTexture = null; - ASDebug.Log($"Package {package.Key} has no thumbnail. Returning default image"); - onSuccess?.Invoke(package.Key, fallbackTexture); - continue; - } - - if (useCached && - AssetStoreCache.GetCachedTexture(package.Key, - out Texture2D texture)) // Try returning cached thumbnails first - { - ASDebug.Log($"Returning cached thumbnail for package {package.Key}"); - onSuccess?.Invoke(package.Key, texture); - continue; - } - - var textureBytes = - await DownloadPackageThumbnail(package.Value["icon_url"].AsString()); - Texture2D tex = new Texture2D(1, 1, TextureFormat.RGBA32, false); - tex.LoadImage(textureBytes); - AssetStoreCache.CacheTexture(package.Key, tex); - ASDebug.Log($"Returning downloaded thumbnail for package {package.Key}"); - onSuccess?.Invoke(package.Key, tex); - } - catch (OperationCanceledException) - { - DisposeDownloadCancellation(); - ASDebug.Log("Package thumbnail download operation cancelled"); - return; - } - catch (Exception e) - { - onFail?.Invoke(package.Key, ASError.GetGenericError(e)); - } - finally - { - packageEnum.Dispose(); - } - } - } - - private static async Task DownloadPackageThumbnail(string url) - { - // icon_url is presented without http/https - Uri uri = new Uri($"https:{url}"); - - var textureBytes = await httpClient.GetAsync(uri, s_downloadCancellationSource.Token). - ContinueWith((response) => response.Result.Content.ReadAsByteArrayAsync().Result, s_downloadCancellationSource.Token); - s_downloadCancellationSource.Token.ThrowIfCancellationRequested(); - return textureBytes; - } - - /// - /// Retrieve, update the cache and return the updated data for a previously cached package - /// - public static async Task GetRefreshedPackageData(string packageId) - { - try - { - var refreshedDataJson = await GetPackageDataExtra(); - var refreshedPackage = default(JsonValue); - - // Find the updated package data in the latest data json - foreach (var p in refreshedDataJson["packages"].AsList()) - { - if (p["id"] == packageId) - { - refreshedPackage = p["versions"].AsList()[p["versions"].AsList().Count - 1]; - break; - } - } - - if (refreshedPackage.Equals(default(JsonValue))) - return new APIResult() { Success = false, Error = ASError.GetGenericError(new MissingMemberException($"Unable to find downloaded package data for package id {packageId}")) }; - - // Check if the supplied package id data has been cached and if it contains the corresponding package - if (!AssetStoreCache.GetCachedPackageMetadata(out JsonValue cachedData) || - !cachedData["packages"].AsDict().ContainsKey(packageId)) - return new APIResult() { Success = false, Error = ASError.GetGenericError(new MissingMemberException($"Unable to find cached package id {packageId}")) }; - - var cachedPackage = cachedData["packages"].AsDict()[packageId]; - - // Retrieve the category map - var categoryJson = await GetCategories(true); - var categories = CreateCategoryDictionary(categoryJson); - - // Update the package data - cachedPackage["name"] = refreshedPackage["name"].AsString(); - cachedPackage["status"] = refreshedPackage["status"].AsString(); - cachedPackage["extra_info"].AsDict()["category_info"].AsDict()["id"] = refreshedPackage["category_id"].AsString(); - cachedPackage["extra_info"].AsDict()["category_info"].AsDict()["name"] = - categories.ContainsKey(refreshedPackage["category_id"]) ? categories[refreshedPackage["category_id"].AsString()] : "Unknown"; - cachedPackage["extra_info"].AsDict()["modified"] = refreshedPackage["modified"].AsString(); - cachedPackage["extra_info"].AsDict()["size"] = refreshedPackage["size"].AsString(); - - AssetStoreCache.CachePackageMetadata(cachedData); - return new APIResult() { Success = true, Response = cachedPackage }; - } - catch (OperationCanceledException) - { - ASDebug.Log("Package metadata download operation cancelled"); - DisposeDownloadCancellation(); - return new APIResult() { Success = false, SilentFail = true }; - } - catch (Exception e) - { - return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; - } - } - - /// - /// Retrieve all Unity versions that the given package has already had uploaded content with - /// - /// - /// - /// - public static List GetPackageUploadedVersions(string packageId, string versionId) - { - var versions = new List(); - try - { - // Retrieve the data for already uploaded versions (should prevent interaction with Uploader) - var versionsTask = Task.Run(() => GetAssetStoreData(APIUri("content", $"preview/{packageId}/{versionId}", SavedSessionId))); - if (!versionsTask.Wait(5000)) - throw new TimeoutException("Could not retrieve uploaded versions within a reasonable time interval"); - - var versionsJson = versionsTask.Result; - foreach (var version in versionsJson["content"].AsDict()["unity_versions"].AsList()) - versions.Add(version.AsString()); - } - catch (OperationCanceledException) - { - ASDebug.Log("Package version download operation cancelled"); - DisposeDownloadCancellation(); - } - catch (Exception e) - { - ASDebug.LogError(e); - } - - return versions; - } - - #endregion - - #region Package Upload API - - /// - /// Upload a content file (.unitypackage) to a provided package version - /// - /// - /// Name of the package. Only used for identifying the package in class - /// Path to the .unitypackage file - /// The value of the main content folder for the provided package - /// The local path (relative to the root project folder) of the main content folder for the provided package - /// The path to the project that this package was built from - /// - public static async Task UploadPackageAsync(string versionId, string packageName, string filePath, - string localPackageGuid, string localPackagePath, string localProjectPath) - { - try - { - ASDebug.Log("Upload task starting"); - EditorApplication.LockReloadAssemblies(); - - if (!IsUploading) // Only subscribe before the first upload - EditorApplication.playModeStateChanged += EditorPlayModeStateChangeHandler; - - var progressData = new OngoingUpload(versionId, packageName); - ActiveUploads.TryAdd(versionId, progressData); - - var result = await Task.Run(() => UploadPackageTask(progressData, filePath, localPackageGuid, localPackagePath, localProjectPath)); - - ActiveUploads.TryRemove(versionId, out OngoingUpload _); - - ASDebug.Log("Upload task finished"); - return result; - } - catch (Exception e) - { - ASDebug.LogError("Upload task failed with an exception: " + e); - ActiveUploads.TryRemove(versionId, out OngoingUpload _); - return PackageUploadResult.PackageUploadFail(ASError.GetGenericError(e)); - } - finally - { - if (!IsUploading) // Only unsubscribe after the last upload - EditorApplication.playModeStateChanged -= EditorPlayModeStateChangeHandler; - - EditorApplication.UnlockReloadAssemblies(); - } - } - - private static PackageUploadResult UploadPackageTask(OngoingUpload currentUpload, string filePath, - string localPackageGuid, string localPackagePath, string localProjectPath) - { - ASDebug.Log("Preparing to upload package within API"); - string api = "asset-store-tools"; - string uri = $"package/{currentUpload.VersionId}/unitypackage"; - - Dictionary packageParams = new Dictionary - { - // Note: project_path is currently used to store UI selections - {"root_guid", localPackageGuid}, - {"root_path", localPackagePath}, - {"project_path", localProjectPath} - }; - - ASDebug.Log($"Creating upload request for {currentUpload.VersionId} {currentUpload.PackageName}"); - - FileStream requestFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - - bool responseTimedOut = false; - long chunkSize = 32768; - try - { - ASDebug.Log("Starting upload process..."); - - var content = new StreamContent(requestFileStream, (int)chunkSize); - var response = httpClient.PutAsync(APIUri(api, uri, SavedSessionId, packageParams), content, currentUpload.CancellationToken); - - // Progress tracking - int updateIntervalMs = 100; - bool allBytesSent = false; - DateTime timeOfCompletion = default(DateTime); - - while (!response.IsCompleted) - { - float uploadProgress = (float)requestFileStream.Position / requestFileStream.Length * 100; - currentUpload.UpdateProgress(uploadProgress); - Thread.Sleep(updateIntervalMs); - - // A timeout for rare cases, when package uploading reaches 100%, but PutAsync task IsComplete value remains 'False' - if (requestFileStream.Position == requestFileStream.Length) - { - if (!allBytesSent) - { - allBytesSent = true; - timeOfCompletion = DateTime.UtcNow; - } - else if (DateTime.UtcNow.Subtract(timeOfCompletion).TotalMilliseconds > UploadResponseTimeoutMs) - { - responseTimedOut = true; - currentUpload.Cancel(); - break; - } - } - } - - // 2020.3 - although cancellation token shows a requested cancellation, the HttpClient - // tends to return a false 'IsCanceled' value, thus yielding an exception when attempting to read the response. - // For now we'll just check the token as well, but this needs to be investigated later on. - if (response.IsCanceled || currentUpload.CancellationToken.IsCancellationRequested) - currentUpload.CancellationToken.ThrowIfCancellationRequested(); - - var responseString = response.Result.Content.ReadAsStringAsync().Result; - - var success = JSONParser.AssetStoreResponseParse(responseString, out ASError error, out JsonValue json); - ASDebug.Log("Upload response JSON: " + json.ToString()); - if (success) - return PackageUploadResult.PackageUploadSuccess(); - else - return PackageUploadResult.PackageUploadFail(error); - } - catch (OperationCanceledException) - { - // Uploading is canceled - if (!responseTimedOut) - { - ASDebug.Log("Upload operation cancelled"); - return PackageUploadResult.PackageUploadCancelled(); - } - else - { - ASDebug.LogWarning("All data has been uploaded, but waiting for the response timed out"); - return PackageUploadResult.PackageUploadResponseTimeout(); - } - } - catch (Exception e) - { - ASDebug.LogError("Upload operation encountered an undefined exception: " + e); - var fullError = e.InnerException != null ? ASError.GetGenericError(e.InnerException) : ASError.GetGenericError(e); - return PackageUploadResult.PackageUploadFail(fullError); - } - finally - { - requestFileStream.Dispose(); - currentUpload.Dispose(); - } - } - - /// - /// Cancel the uploading task for a package with the provided package id - /// - public static void AbortPackageUpload(string packageId) - { - ActiveUploads[packageId]?.Cancel(); - } - - #endregion - - #region Utility Methods - - public static async Task GetLatestAssetStoreToolsVersion() - { - try - { - var url = "https://api.assetstore.unity3d.com/package/latest-version/115"; - var result = await httpClient.GetAsync(url); - - result.EnsureSuccessStatusCode(); - - var resultStr = await result.Content.ReadAsStringAsync(); - - var json = JSONParser.SimpleParse(resultStr); - - return new APIResult() { Success = true, Response = json }; - } - catch (Exception e) - { - return new APIResult() { Success = false, Error = ASError.GetGenericError(e) }; - } - } - - private static string GetLicenseHash() - { - return UnityEditorInternal.InternalEditorUtility.GetAuthToken().Substring(0, 40); - } - - private static string GetHardwareHash() - { - return UnityEditorInternal.InternalEditorUtility.GetAuthToken().Substring(40, 40); - } - - private static FormUrlEncodedContent GetLoginContent(Dictionary loginData) - { - loginData.Add("unityversion", Application.unityVersion); - loginData.Add("toolversion", ToolVersion); - loginData.Add("license_hash", GetLicenseHash()); - loginData.Add("hardware_hash", GetHardwareHash()); - - return new FormUrlEncodedContent(loginData); - } - - private static async Task GetAssetStoreData(Uri uri) - { - SetupDownloadCancellation(); - - var response = await httpClient.GetAsync(uri, s_downloadCancellationSource.Token) - .ContinueWith((x) => x.Result.Content.ReadAsStringAsync().Result, s_downloadCancellationSource.Token); - s_downloadCancellationSource.Token.ThrowIfCancellationRequested(); - - if (!JSONParser.AssetStoreResponseParse(response, out var error, out var jsonMainData)) - throw error.Exception; - - return jsonMainData; - } - - private static Uri APIUri(string apiPath, string endPointPath, string sessionId) - { - return APIUri(apiPath, endPointPath, sessionId, null); - } - - // Method borrowed from A$ tools, could maybe be simplified to only retain what is necessary? - private static Uri APIUri(string apiPath, string endPointPath, string sessionId, IDictionary extraQuery) - { - Dictionary extraQueryMerged; - - if (extraQuery == null) - extraQueryMerged = new Dictionary(); - else - extraQueryMerged = new Dictionary(extraQuery); - - extraQueryMerged.Add("unityversion", Application.unityVersion); - extraQueryMerged.Add("toolversion", ToolVersion); - extraQueryMerged.Add("xunitysession", sessionId); - - string uriPath = $"{AssetStoreProdUrl}/api/{apiPath}/{endPointPath}.json"; - UriBuilder uriBuilder = new UriBuilder(uriPath); - - StringBuilder queryToAppend = new StringBuilder(); - foreach (KeyValuePair queryPair in extraQueryMerged) - { - string queryName = queryPair.Key; - string queryValue = Uri.EscapeDataString(queryPair.Value); - - queryToAppend.AppendFormat("&{0}={1}", queryName, queryValue); - } - if (!string.IsNullOrEmpty(uriBuilder.Query)) - uriBuilder.Query = uriBuilder.Query.Substring(1) + queryToAppend; - else - uriBuilder.Query = queryToAppend.Remove(0, 1).ToString(); - - return uriBuilder.Uri; - } - - private static JsonValue MergePackageData(JsonValue mainPackageData, JsonValue extraPackageData, JsonValue categoryData) - { - ASDebug.Log($"Main package data\n{mainPackageData}"); - var mainDataDict = mainPackageData["packages"].AsDict(); - - // Most likely both of them will be true at the same time, but better to be safe - if (mainDataDict.Count == 0 || !extraPackageData.ContainsKey("packages")) - return new JsonValue(); - - ASDebug.Log($"Extra package data\n{extraPackageData}"); - var extraDataDict = extraPackageData["packages"].AsList(); - - var categories = CreateCategoryDictionary(categoryData); - - foreach (var md in mainDataDict) - { - foreach (var ed in extraDataDict) - { - if (ed["id"].AsString() != md.Key) - continue; - - // Create a field for extra data - var extraData = JsonValue.NewDict(); - - // Add category field - var categoryEntry = JsonValue.NewDict(); - - var categoryId = ed["category_id"].AsString(); - var categoryName = categories.ContainsKey(categoryId) ? categories[categoryId] : "Unknown"; - - categoryEntry["id"] = categoryId; - categoryEntry["name"] = categoryName; - - extraData["category_info"] = categoryEntry; - - // Add modified time and size - var versions = ed["versions"].AsList(); - extraData["modified"] = versions[versions.Count - 1]["modified"]; - extraData["size"] = versions[versions.Count - 1]["size"]; - - md.Value.AsDict()["extra_info"] = extraData; - } - } - - mainPackageData.AsDict()["packages"] = new JsonValue(mainDataDict); - return mainPackageData; - } - - private static Dictionary CreateCategoryDictionary(JsonValue json) - { - var categories = new Dictionary(); - - var list = json.AsList(); - - for (int i = 0; i < list.Count; i++) - { - var category = list[i].AsDict(); - if (category["status"].AsString() == "deprecated") - continue; - categories.Add(category["id"].AsString(), category["assetstore_name"].AsString()); - } - - return categories; - } - - /// - /// Check if the account data is for a valid publisher account - /// - /// Json structure retrieved from one of the API login methods - public static bool IsPublisherValid(JsonValue json, out ASError error) - { - error = ASError.GetPublisherNullError(json["name"]); - - if (!json.ContainsKey("publisher")) - return false; - - // If publisher account is not created - let them know - return !json["publisher"].IsNull(); - } - - /// - /// Cancel all data retrieval tasks - /// - public static void AbortDownloadTasks() - { - s_downloadCancellationSource?.Cancel(); - } - - /// - /// Cancel all data uploading tasks - /// - public static void AbortUploadTasks() - { - foreach (var upload in ActiveUploads) - { - AbortPackageUpload(upload.Key); - } - } - - private static void SetupDownloadCancellation() - { - if (s_downloadCancellationSource != null && s_downloadCancellationSource.IsCancellationRequested) - DisposeDownloadCancellation(); - - if (s_downloadCancellationSource == null) - s_downloadCancellationSource = new CancellationTokenSource(); - } - - private static void DisposeDownloadCancellation() - { - s_downloadCancellationSource?.Dispose(); - s_downloadCancellationSource = null; - } - - private static void EditorPlayModeStateChangeHandler(PlayModeStateChange state) - { - if (state != PlayModeStateChange.ExitingEditMode) - return; - - EditorApplication.ExitPlaymode(); - EditorUtility.DisplayDialog("Notice", "Entering Play Mode is not allowed while there's a package upload in progress.\n\n" + - "Please wait until the upload is finished or cancel the upload from the Asset Store Uploader window", "OK"); - } - - private static void OverrideAssetStoreUrl() - { - var args = Environment.GetCommandLineArgs(); - for (var i = 0; i < args.Length; i++) - { - if (!args[i].Equals("-assetStoreUrl")) - continue; - - if (i + 1 >= args.Length) - return; - - ASDebug.Log($"Overriding A$ URL to: {args[i + 1]}"); - AssetStoreProdUrl = args[i + 1]; - return; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta index be3554a..1382bf8 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e3cae7082463da41b807724242fd617 +guid: 930cfc857321b1048bf9607d4c8f89d3 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs deleted file mode 100644 index b809abb..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using UnityEditor; -using UnityEngine.Analytics; - -namespace AssetStoreTools.Uploader.Data -{ - internal static class ASAnalytics - { - private const int VersionId = 3; - private const int MaxEventsPerHour = 20; - private const int MaxNumberOfElements = 1000; - - private const string VendorKey = "unity.assetStoreTools"; - private const string EventName = "assetStoreTools"; - - static bool EnableAnalytics() - { -#if UNITY_2023_2_OR_NEWER - return true; -#else - var result = EditorAnalytics.RegisterEventWithLimit(EventName, MaxEventsPerHour, MaxNumberOfElements, VendorKey, VersionId); - return result == AnalyticsResult.Ok; -#endif - } - - [System.Serializable] - public struct AnalyticsData -#if UNITY_2023_2_OR_NEWER - : IAnalytic.IData -#endif - { - public string ToolVersion; - public string PackageId; - public string Category; - public bool UsedValidator; - public string ValidatorResults; - public string UploadFinishedReason; - public double TimeTaken; - public long PackageSize; - public string Workflow; - public string EndpointUrl; - } - -#if UNITY_2023_2_OR_NEWER - [AnalyticInfo(eventName: EventName, vendorKey: VendorKey, version: VersionId, maxEventsPerHour: MaxEventsPerHour, maxNumberOfElements: MaxNumberOfElements)] - private class AssetStoreToolsAnalytic : IAnalytic - { - private AnalyticsData _data; - - public AssetStoreToolsAnalytic(AnalyticsData data) - { - _data = data; - } - - public bool TryGatherData(out IAnalytic.IData data, out Exception error) - { - error = null; - data = _data; - return data != null; - } - } -#endif - - public static void SendUploadingEvent(AnalyticsData data) - { - if (!EditorAnalytics.enabled) - return; - - if (!EnableAnalytics()) - return; - -#if UNITY_2023_2_OR_NEWER - var analytic = new AssetStoreToolsAnalytic(data); - EditorAnalytics.SendAnalytic(analytic); -#else - EditorAnalytics.SendEventWithLimit(EventName, data, VersionId); -#endif - } - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs.meta deleted file mode 100644 index 396fef2..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: 1095145789a64767a6add837eea19786 -timeCreated: 1658832954 -AssetOrigin: - serializedVersion: 1 - productId: 115 - packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/ASAnalytics.cs - uploadId: 712972 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions.meta new file mode 100644 index 0000000..73728d0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 771776e4d51c47945b3449d4de948c00 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs new file mode 100644 index 0000000..84033b5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs @@ -0,0 +1,34 @@ +using System; +using UnityEngine; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IPackage + { + string PackageId { get; } + string VersionId { get; } + string Name { get; } + string Status { get; } + string Category { get; } + bool IsCompleteProject { get; } + string RootGuid { get; } + string RootPath { get; } + string ProjectPath { get; } + string Modified { get; } + string Size { get; } + bool IsDraft { get; } + Texture2D Icon { get; } + + event Action OnUpdate; + event Action OnIconUpdate; + + string FormattedSize(); + string FormattedModified(); + + void UpdateData(PackageModel source); + void UpdateIcon(Texture2D texture); + + PackageModel ToModel(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs.meta new file mode 100644 index 0000000..190b689 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b92f2ed98d0b31a479aa2bfd95528fbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackage.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs new file mode 100644 index 0000000..d82520f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IPackageContent + { + event Action OnActiveWorkflowChanged; + + IWorkflow GetActiveWorkflow(); + List GetAvailableWorkflows(); + void SetActiveWorkflow(IWorkflow workflow); + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs.meta new file mode 100644 index 0000000..6142045 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 45ce41158c3174149b7056a30ac901db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageContent.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs new file mode 100644 index 0000000..c35936b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IPackageGroup + { + string Name { get; } + List Packages { get; } + + event Action> OnPackagesSorted; + event Action> OnPackagesFiltered; + + void Sort(PackageSorting sortingType); + void Filter(string filter); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs.meta new file mode 100644 index 0000000..4a35808 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f683845071b8891498156d95a1a5c2dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IPackageGroup.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs new file mode 100644 index 0000000..c6e9b81 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs @@ -0,0 +1,36 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Exporter; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IWorkflow + { + string Name { get; } + string DisplayName { get; } + string PackageName { get; } + string PackageExtension { get; } + bool IsPathSet { get; } + + event Action OnChanged; + event Action OnUploadStateChanged; + + bool GenerateHighQualityPreviews { get; set; } + ValidationSettings LastValidationSettings { get; } + ValidationResult LastValidationResult { get; } + + IEnumerable GetAllPaths(); + ValidationResult Validate(); + Task ExportPackage(string outputPath); + Task ValidatePackageUploadedVersions(); + + Task UploadPackage(string exportedPackagePath); + void AbortUpload(); + void ResetUploadStatus(); + Task RefreshPackage(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs.meta new file mode 100644 index 0000000..c8381f3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7a2f796eadafa774bae89cf3939611dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflow.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs new file mode 100644 index 0000000..6987b91 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs @@ -0,0 +1,18 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using System; +using System.Threading.Tasks; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Data +{ + internal interface IWorkflowServices + { + Task GetPackageUploadedVersions(IPackage package, int timeoutMs); + Task UploadPackage(IPackageUploader uploader, IProgress progress); + void StopUploading(IPackageUploader uploader); + AnalyticsResult SendAnalytic(IAssetStoreAnalytic data); + Task UpdatePackageData(IPackage package); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs.meta new file mode 100644 index 0000000..88c381c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0ae017363fa41ff4d9926dc4a5852246 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/IWorkflowServices.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs new file mode 100644 index 0000000..cd3462a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs @@ -0,0 +1,253 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Exporter; +using AssetStoreTools.Previews; +using AssetStoreTools.Previews.Data; +using AssetStoreTools.Previews.Generators; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UnityEngine; + +namespace AssetStoreTools.Uploader.Data +{ + internal abstract class WorkflowBase : IWorkflow + { + protected IPackage Package; + + public abstract string Name { get; } + public abstract string DisplayName { get; } + public string PackageName => Package.Name; + public abstract string PackageExtension { get; } + public abstract bool IsPathSet { get; } + + protected string LocalPackageGuid; + protected string LocalPackagePath; + protected string LocalProjectPath; + + public bool GenerateHighQualityPreviews { get; set; } + public ValidationSettings LastValidationSettings { get; private set; } + public ValidationResult LastValidationResult { get; private set; } + + private IWorkflowServices _services; + private IPackageUploader _activeUploader; + + public abstract event Action OnChanged; + public event Action OnUploadStateChanged; + + public WorkflowBase(IPackage package, IWorkflowServices services) + { + Package = package; + _services = services; + } + + public abstract IEnumerable GetAllPaths(); + + public abstract IValidator CreateValidator(); + + public ValidationResult Validate() + { + var validator = CreateValidator(); + var result = CreateValidator().Validate(); + + LastValidationSettings = validator.Settings; + LastValidationResult = result; + + return result; + } + + protected IPreviewGenerator CreatePreviewGenerator(List inputPaths) + { + PreviewGenerationSettings settings; + IPreviewGenerator generator; + + // Filter out ProjectSettings + inputPaths = inputPaths.Where(x => x == "Assets" || x.StartsWith("Assets/") || x.StartsWith("Packages/")).ToList(); + + if (!GenerateHighQualityPreviews) + { + settings = new NativePreviewGenerationSettings() + { + InputPaths = inputPaths.ToArray(), + OverwriteExisting = false, + OutputPath = Constants.Previews.Native.DefaultOutputPath, + Format = Constants.Previews.Native.DefaultFormat, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + WaitForPreviews = Constants.Previews.Native.DefaultWaitForPreviews, + ChunkedPreviewLoading = Constants.Previews.Native.DefaultChunkedPreviewLoading, + ChunkSize = Constants.Previews.Native.DefaultChunkSize + }; + + generator = new NativePreviewGenerator((NativePreviewGenerationSettings)settings); + } + else + { + settings = new CustomPreviewGenerationSettings() + { + InputPaths = inputPaths.ToArray(), + OverwriteExisting = false, + Width = Constants.Previews.Custom.DefaultWidth, + Height = Constants.Previews.Custom.DefaultHeight, + Depth = Constants.Previews.Custom.DefaultDepth, + NativeWidth = Constants.Previews.Custom.DefaultNativeWidth, + NativeHeight = Constants.Previews.Custom.DefaultNativeHeight, + OutputPath = Constants.Previews.Custom.DefaultOutputPath, + Format = Constants.Previews.Custom.DefaultFormat, + PreviewFileNamingFormat = Constants.Previews.DefaultFileNameFormat, + AudioSampleColor = Constants.Previews.Custom.DefaultAudioSampleColor, + AudioBackgroundColor = Constants.Previews.Custom.DefaultAudioBackgroundColor, + }; + + generator = new CustomPreviewGenerator((CustomPreviewGenerationSettings)settings); + } + + return generator; + } + + public abstract IPackageExporter CreateExporter(string outputPath); + + public virtual async Task ExportPackage(string outputPath) + { + var exporter = CreateExporter(outputPath); + var result = await exporter.Export(); + return result; + } + + public async Task ValidatePackageUploadedVersions() + { + var unityVersionSupported = string.Compare(Application.unityVersion, Constants.Uploader.MinRequiredUnitySupportVersion, StringComparison.Ordinal) >= 0; + if (unityVersionSupported) + return true; + + var response = await _services.GetPackageUploadedVersions(Package, 5000); + if (response.Cancelled || response.Success == false) + return true; + + return response.UnityVersions.Any(x => string.Compare(x, Constants.Uploader.MinRequiredUnitySupportVersion, StringComparison.Ordinal) >= 0); + } + + private bool ValidatePackageBeforeUpload(string packagePath, out string error) + { + error = string.Empty; + + if (!File.Exists(packagePath)) + { + error = $"File '{packagePath}' was not found."; + return false; + } + + if (!ValidatePackageSize(packagePath, out error)) + { + return false; + } + + return true; + } + + private bool ValidatePackageSize(string packagePath, out string error) + { + error = string.Empty; + + long packageSize = new FileInfo(packagePath).Length; + long packageSizeLimit = Constants.Uploader.MaxPackageSizeBytes; + + if (packageSize <= packageSizeLimit) + return true; + + float packageSizeInGB = packageSize / (float)1073741824; // (1024 * 1024 * 1024) + float maxPackageSizeInGB = packageSizeLimit / (float)1073741824; + error = $"The size of your package ({packageSizeInGB:0.0} GB) exceeds the maximum allowed package size of {maxPackageSizeInGB:0} GB. " + + $"Please reduce the size of your package."; + + return false; + } + + public async Task UploadPackage(string packagePath) + { + if (!ValidatePackageBeforeUpload(packagePath, out var error)) + { + return new PackageUploadResponse() { Success = false, Status = UploadStatus.Fail, Exception = new Exception(error) }; + } + + _activeUploader = CreatePackageUploader(packagePath); + var progress = new Progress(); + + var time = System.Diagnostics.Stopwatch.StartNew(); + + progress.ProgressChanged += ReportUploadProgress; + var response = await _services.UploadPackage(_activeUploader, progress); + progress.ProgressChanged -= ReportUploadProgress; + + // Send analytics + time.Stop(); + if (!response.Cancelled) + SendAnalytics(packagePath, response.Status, time.Elapsed.TotalSeconds); + + OnUploadStateChanged?.Invoke(response.Status, null); + _activeUploader = null; + return response; + } + + protected abstract IPackageUploader CreatePackageUploader(string exportedPackagePath); + + private void ReportUploadProgress(object _, float value) + { + OnUploadStateChanged?.Invoke(null, value); + } + + private void SendAnalytics(string packagePath, UploadStatus uploadStatus, double timeTakenSeconds) + { + try + { + var analytic = new PackageUploadAnalytic( + packageId: Package.PackageId, + category: Package.Category, + usedValidator: LastValidationResult != null, + validationSettings: LastValidationSettings, + validationResult: LastValidationResult, + uploadFinishedReason: uploadStatus, + timeTaken: timeTakenSeconds, + packageSize: new FileInfo(packagePath).Length, + workflow: Name + ); + + var result = _services.SendAnalytic(analytic); + } + catch (Exception e) { ASDebug.LogError($"Could not send analytics: {e}"); } + } + + public void AbortUpload() + { + if (_activeUploader != null) + _services.StopUploading(_activeUploader); + + _activeUploader = null; + } + + public void ResetUploadStatus() + { + OnUploadStateChanged?.Invoke(UploadStatus.Default, 0f); + } + + public async Task RefreshPackage() + { + var response = await _services.UpdatePackageData(Package); + if (!response.Success) + return; + + Package.UpdateData(response.Package); + } + + public abstract bool IsPathValid(string path, out string reason); + + protected abstract void Serialize(); + + protected abstract void Deserialize(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs.meta new file mode 100644 index 0000000..a7a6a65 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d0e87ee17aa944c42b1c335abe19daaf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs new file mode 100644 index 0000000..967bd9b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs @@ -0,0 +1,329 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; + +namespace AssetStoreTools.Uploader.Data +{ + internal class AssetsWorkflow : WorkflowBase + { + public override string Name => "AssetsWorkflow"; + public override string DisplayName => "From Assets Folder"; + public override string PackageExtension => ".unitypackage"; + public override bool IsPathSet => !string.IsNullOrEmpty(_mainExportPath); + public bool IsCompleteProject => Package.IsCompleteProject; + + private AssetsWorkflowState _stateData; + + private string _mainExportPath; + private bool _includeDependencies; + private List _dependencies; + private List _specialFolders; + + public override event Action OnChanged; + + // Special folders that would not work if not placed directly in the 'Assets' folder + private readonly string[] _extraAssetFolderNames = + { + "Editor Default Resources", "Gizmos", "Plugins", + "StreamingAssets", "Standard Assets", "WebGLTemplates", + "ExternalDependencyManager", "XR" + }; + + public AssetsWorkflow(IPackage package, AssetsWorkflowState stateData, IWorkflowServices services) + : base(package, services) + { + _stateData = stateData; + Deserialize(); + } + + public string GetMainExportPath() + { + return _mainExportPath; + } + + public void SetMainExportPath(string path, bool serialize) + { + _mainExportPath = path; + SetMetadata(); + if (serialize) + Serialize(); + } + + private void SetMetadata() + { + LocalPackageGuid = AssetDatabase.AssetPathToGUID(_mainExportPath); + LocalPackagePath = _mainExportPath; + LocalProjectPath = _mainExportPath; + } + + public bool GetIncludeDependencies() + { + return _includeDependencies; + } + + public void SetIncludeDependencies(bool value, bool serialize) + { + _includeDependencies = value; + // Note: make sure that exporting does not fail when + // a serialized dependency that has been removed from a project is sent to exporter + if (serialize) + Serialize(); + } + + public List GetDependencies() + { + return _dependencies; + } + + public void SetDependencies(IEnumerable dependencies, bool serialize) + { + _dependencies.Clear(); + foreach (var dependency in dependencies) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + _dependencies.Add(package); + } + + if (serialize) + Serialize(); + } + + public List GetSpecialFolders() + { + return _specialFolders; + } + + public void SetSpecialFolders(IEnumerable specialFolders, bool serialize) + { + _specialFolders.Clear(); + foreach (var folder in specialFolders) + { + _specialFolders.Add(folder); + } + + if (serialize) + Serialize(); + } + + public override bool IsPathValid(string path, out string error) + { + error = string.Empty; + + var pathIsFolder = Directory.Exists(path); + if (!pathIsFolder) + { + error = "Path must point to a valid folder"; + return false; + } + + var pathWithinAssetsFolder = path.StartsWith("Assets/") && path != "Assets/"; + if (pathWithinAssetsFolder) + return true; + + var pathIsAssetsFolder = path == "Assets" || path == "Assets/"; + if (pathIsAssetsFolder) + { + var assetsFolderSelectionAllowed = Package.IsCompleteProject; + if (assetsFolderSelectionAllowed) + return true; + + error = "'Assets' folder is only available for packages tagged as a 'Complete Project'."; + return false; + } + + error = "Selected folder path must be within the project's Assets."; + return false; + } + + public List GetAvailableDependencies() + { + var registryPackages = PackageUtility.GetAllRegistryPackages(); + return registryPackages.Select(x => x.name).ToList(); + } + + public List GetAvailableSpecialFolders() + { + var specialFolders = new List(); + + foreach (var extraAssetFolderName in _extraAssetFolderNames) + { + var fullExtraPath = "Assets/" + extraAssetFolderName; + + if (!Directory.Exists(fullExtraPath)) + continue; + + if (_mainExportPath.ToLower().StartsWith(fullExtraPath.ToLower())) + continue; + + // Don't include nested paths + if (!fullExtraPath.ToLower().StartsWith(_mainExportPath.ToLower())) + specialFolders.Add(fullExtraPath); + } + + return specialFolders; + } + + public override IEnumerable GetAllPaths() + { + var paths = new List() + { + _mainExportPath + }; + paths.AddRange(GetSpecialFolders()); + + return paths; + } + + public override IValidator CreateValidator() + { + var validationPaths = GetAllPaths(); + + var validationSettings = new CurrentProjectValidationSettings() + { + Category = Package.Category, + ValidationPaths = validationPaths.ToList(), + ValidationType = ValidationType.UnityPackage + }; + + var validator = new CurrentProjectValidator(validationSettings); + return validator; + } + + public override IPackageExporter CreateExporter(string outputPath) + { + var exportPaths = GetAllPaths().ToList(); + + if (IsCompleteProject && !exportPaths.Contains("ProjectSettings")) + { + exportPaths.Add("ProjectSettings"); + } + + var dependenciesToInclude = new List(); + if (_includeDependencies) + { + dependenciesToInclude.AddRange(_dependencies.Select(x => x.name)); + } + + if (ASToolsPreferences.Instance.UseLegacyExporting) + { + var exportSettings = new LegacyExporterSettings() + { + ExportPaths = exportPaths.ToArray(), + OutputFilename = outputPath, + IncludeDependencies = _includeDependencies, + }; + + return new LegacyPackageExporter(exportSettings); + } + else + { + var exportSettings = new DefaultExporterSettings() + { + ExportPaths = exportPaths.ToArray(), + OutputFilename = outputPath, + Dependencies = dependenciesToInclude.ToArray(), + PreviewGenerator = CreatePreviewGenerator(exportPaths), + }; + + return new DefaultPackageExporter(exportSettings); + } + } + + protected override IPackageUploader CreatePackageUploader(string exportedPackagePath) + { + var uploaderSettings = new UnityPackageUploadSettings() + { + UnityPackagePath = exportedPackagePath, + VersionId = Package.VersionId, + RootGuid = LocalPackageGuid, + RootPath = LocalPackagePath, + ProjectPath = LocalProjectPath + }; + + var uploader = new UnityPackageUploader(uploaderSettings); + return uploader; + } + + protected override void Serialize() + { + _stateData.SetMainPath(_mainExportPath); + _stateData.SetIncludeDependencies(_includeDependencies); + _stateData.SetDependencies(_dependencies.Select(x => x.name)); + _stateData.SetSpecialFolders(_specialFolders); + OnChanged?.Invoke(); + } + + protected override void Deserialize() + { + _mainExportPath = _stateData.GetMainPath(); + + _specialFolders = new List(); + foreach (var path in _stateData.GetSpecialFolders()) + { + _specialFolders.Add(path); + } + + _includeDependencies = _stateData.GetIncludeDependencies(); + + _dependencies = new List(); + foreach (var dependency in _stateData.GetDependencies()) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + + _dependencies.Add(package); + } + + DeserializeFromUploadedData(); + } + + private void DeserializeFromUploadedData() + { + DeserializeFromUploadedDataByGuid(); + DeserializeFromUploadedDataByPath(); + } + + private void DeserializeFromUploadedDataByGuid() + { + if (!string.IsNullOrEmpty(_mainExportPath)) + return; + + var lastUploadedGuid = Package.RootGuid; + if (string.IsNullOrEmpty(lastUploadedGuid)) + return; + + var potentialPackagePath = AssetDatabase.GUIDToAssetPath(lastUploadedGuid); + DeserializeFromUploadedDataByPath(potentialPackagePath); + } + + private void DeserializeFromUploadedDataByPath() + { + if (!string.IsNullOrEmpty(_mainExportPath)) + return; + + var lastUploadedPath = Package.ProjectPath; + if (string.IsNullOrEmpty(lastUploadedPath)) + return; + + DeserializeFromUploadedDataByPath(lastUploadedPath); + } + + private void DeserializeFromUploadedDataByPath(string path) + { + if (string.IsNullOrEmpty(path) || !IsPathValid(path, out var _)) + return; + + _mainExportPath = path; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility/AssetStoreCache.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs.meta similarity index 75% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility/AssetStoreCache.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs.meta index c6ec484..4e07adf 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility/AssetStoreCache.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2e5fee0cad7655f458d9b600f4ae6d02 +guid: 4657d35aaf9d70948a0840dc615f64ec MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Utility/AssetStoreCache.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/AssetsWorkflow.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs new file mode 100644 index 0000000..3759313 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs @@ -0,0 +1,251 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Utility; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.PackageManager; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; +using PackageManager = UnityEditor.PackageManager; + +namespace AssetStoreTools.Uploader.Data +{ + internal class HybridPackageWorkflow : WorkflowBase + { + public override string Name => "HybridPackageWorkflow"; + public override string DisplayName => "Local UPM Package"; + public override string PackageExtension => ".unitypackage"; + public override bool IsPathSet => _packageInfo != null; + + private HybridPackageWorkflowState _stateData; + + private PackageInfo _packageInfo; + private List _dependencies; + + public override event Action OnChanged; + + public HybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData, IWorkflowServices services) + : base(package, services) + { + _stateData = stateData; + Deserialize(); + } + + public PackageInfo GetPackage() + { + return _packageInfo; + } + + public void SetPackage(PackageInfo packageInfo, bool serialize) + { + if (packageInfo == null) + throw new ArgumentException("Package is null"); + + _packageInfo = packageInfo; + SetMetadata(); + if (serialize) + Serialize(); + } + + public void SetPackage(string packageManifestPath, bool serialize) + { + if (!PackageUtility.GetPackageByManifestPath(packageManifestPath, out var package)) + throw new ArgumentException("Path does not correspond to a valid package"); + + SetPackage(package, serialize); + } + + private void SetMetadata() + { + LocalPackageGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(_packageInfo.GetManifestAsset())); + LocalPackagePath = _packageInfo.assetPath; + LocalProjectPath = _packageInfo.name; + } + + public List GetDependencies() + { + return _dependencies; + } + + public void SetDependencies(IEnumerable dependencies, bool serialize) + { + _dependencies.Clear(); + foreach (var dependency in dependencies) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + _dependencies.Add(package); + } + + if (serialize) + Serialize(); + } + + public List GetAvailableDependencies() + { + var availableDependencies = new List(); + if (_packageInfo == null) + return availableDependencies; + + var packageDependencies = _packageInfo.dependencies.Select(x => x.name); + foreach (var dependency in packageDependencies) + { + if (!PackageUtility.GetPackageByPackageName(dependency, out var package)) + continue; + + if (package.source != PackageManager.PackageSource.Local + && package.source != PackageManager.PackageSource.Embedded) + continue; + + availableDependencies.Add(package); + } + + return availableDependencies; + } + + public override IEnumerable GetAllPaths() + { + var paths = new List(); + + if (_packageInfo == null) + return paths; + + paths.Add(_packageInfo.assetPath); + paths.AddRange(_dependencies.Select(x => x.assetPath)); + + return paths; + } + + public override bool IsPathValid(string path, out string reason) + { + reason = string.Empty; + + if (!PackageUtility.GetPackageByManifestPath(path, out var package)) + { + reason = "Selected path must point to a package manifest for a package that is imported into the current project"; + return false; + } + + var packageSourceValid = package.source == PackageSource.Embedded || package.source == PackageSource.Local; + if (!packageSourceValid) + { + reason = "Selected package must be a local or an embedded package"; + return false; + } + + return true; + } + + public override IValidator CreateValidator() + { + var validationPaths = GetAllPaths(); + + var validationSettings = new CurrentProjectValidationSettings() + { + Category = Package?.Category, + ValidationPaths = validationPaths.ToList(), + ValidationType = ValidationType.UnityPackage + }; + + var validator = new CurrentProjectValidator(validationSettings); + return validator; + } + + public override IPackageExporter CreateExporter(string outputPath) + { + var exportPaths = GetAllPaths(); + + var exportSettings = new DefaultExporterSettings() + { + ExportPaths = exportPaths.ToArray(), + OutputFilename = outputPath, + PreviewGenerator = CreatePreviewGenerator(exportPaths.ToList()) + }; + + return new DefaultPackageExporter(exportSettings); + } + + protected override IPackageUploader CreatePackageUploader(string exportedPackagePath) + { + var uploaderSettings = new UnityPackageUploadSettings() + { + UnityPackagePath = exportedPackagePath, + VersionId = Package.VersionId, + RootGuid = LocalPackageGuid, + RootPath = LocalPackagePath, + ProjectPath = LocalProjectPath + }; + + var uploader = new UnityPackageUploader(uploaderSettings); + return uploader; + } + + protected override void Serialize() + { + if (_packageInfo == null) + return; + + _stateData.SetPackageName(_packageInfo.name); + _stateData.SetPackageDependencies(_dependencies.Select(x => x.name).OrderBy(x => x)); + OnChanged?.Invoke(); + } + + protected override void Deserialize() + { + var packageName = _stateData.GetPackageName(); + if (PackageUtility.GetPackageByPackageName(packageName, out var package)) + _packageInfo = package; + + _dependencies = new List(); + var dependencies = _stateData.GetPackageDependencies(); + foreach (var dependency in dependencies) + { + if (PackageUtility.GetPackageByPackageName(dependency, out var packageDependency)) + _dependencies.Add(packageDependency); + } + + DeserializeFromUploadedData(); + } + + private void DeserializeFromUploadedData() + { + DeserializeFromUploadedDataByPackageName(); + DeserializeFromUploadedDataByPackageGuid(); + } + + private void DeserializeFromUploadedDataByPackageName() + { + if (_packageInfo != null) + return; + + var lastUploadedPackageName = Package.ProjectPath; + if (!PackageUtility.GetPackageByPackageName(lastUploadedPackageName, out var package)) + return; + + _packageInfo = package; + } + + private void DeserializeFromUploadedDataByPackageGuid() + { + if (_packageInfo != null) + return; + + var lastUploadedGuid = Package.RootGuid; + if (string.IsNullOrEmpty(lastUploadedGuid)) + return; + + var potentialPackageManifestPath = AssetDatabase.GUIDToAssetPath(lastUploadedGuid); + if (string.IsNullOrEmpty(potentialPackageManifestPath) || !IsPathValid(potentialPackageManifestPath, out var _)) + return; + + if (!PackageUtility.GetPackageByManifestPath(potentialPackageManifestPath, out var package)) + return; + + _packageInfo = package; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs.meta new file mode 100644 index 0000000..8feba57 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3061839aba3894246a20195639eeda1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/HybridPackageWorkflow.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs deleted file mode 100644 index 585e4fc..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Threading; - -namespace AssetStoreTools.Uploader.Data -{ - internal class OngoingUpload : IDisposable - { - private CancellationTokenSource _cancellationTokenSource; - - public string VersionId { get; } - public string PackageName { get; } - public float Progress { get; private set; } - public CancellationToken CancellationToken => _cancellationTokenSource.Token; - - public OngoingUpload(string versionId, string packageName) - { - VersionId = versionId; - PackageName = packageName; - Progress = 0f; - _cancellationTokenSource = new CancellationTokenSource(); - } - - public void Cancel() - { - _cancellationTokenSource?.Cancel(); - } - - public void Dispose() - { - _cancellationTokenSource?.Dispose(); - _cancellationTokenSource = null; - } - - public void UpdateProgress(float newProgress) - { - Progress = newProgress; - } - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs new file mode 100644 index 0000000..1a984e5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs @@ -0,0 +1,91 @@ +using System; +using UnityEngine; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Data +{ + internal class Package : IPackage + { + private PackageModel _source; + + public string PackageId => _source.PackageId; + public string VersionId => _source.VersionId; + public string Name => _source.Name; + public string Status => _source.Status; + public string Category => _source.Category; + public bool IsCompleteProject => _source.IsCompleteProject; + public string RootGuid => _source.RootGuid; + public string RootPath => _source.RootPath; + public string ProjectPath => _source.ProjectPath; + public string Modified => _source.Modified; + public string Size => _source.Size; + public string IconUrl => _source.IconUrl; + public bool IsDraft => Status.Equals("draft", StringComparison.OrdinalIgnoreCase); + public Texture2D Icon { get; private set; } + + public event Action OnUpdate; + public event Action OnIconUpdate; + + public Package(PackageModel packageSource) + { + _source = packageSource; + } + + public void UpdateIcon(Texture2D texture) + { + if (texture == null) + return; + + Icon = texture; + OnIconUpdate?.Invoke(); + } + + public string FormattedSize() + { + var defaultSize = "0.00 MB"; + if (float.TryParse(Size, out var sizeBytes)) + return $"{sizeBytes / (1024f * 1024f):0.00} MB"; + + return defaultSize; + } + + public string FormattedModified() + { + var defaultDate = "Unknown"; + if (DateTime.TryParse(Modified, out var dt)) + return dt.Date.ToString("yyyy-MM-dd"); + + return defaultDate; + } + + public void UpdateData(PackageModel source) + { + if (source == null) + throw new ArgumentException("Provided package is null"); + + _source = source; + OnUpdate?.Invoke(); + } + + public PackageModel ToModel() + { + var model = new PackageModel() + { + PackageId = _source.PackageId, + VersionId = _source.VersionId, + Name = _source.Name, + Status = _source.Status, + Category = _source.Category, + IsCompleteProject = _source.IsCompleteProject, + RootGuid = _source.RootGuid, + RootPath = _source.RootPath, + ProjectPath = _source.ProjectPath, + Modified = _source.Modified, + Size = _source.Size, + IconUrl = _source.IconUrl + }; + + return model; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs.meta similarity index 76% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs.meta index ec951f2..7e4f9df 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 684fca3fffd79d944a32d9b3adbfc007 +guid: fc2198164bbd6394b87c51a74fe2915e MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/AssetStoreAPI.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Package.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs new file mode 100644 index 0000000..bac6c08 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs @@ -0,0 +1,68 @@ +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Uploader.Services; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Uploader.Data +{ + internal class PackageContent : IPackageContent + { + private IWorkflow _activeWorkflow; + private List _workflows; + private WorkflowStateData _workflowStateData; + + private ICachingService _cachingService; + + public event Action OnActiveWorkflowChanged; + + public PackageContent(List workflows, WorkflowStateData workflowStateData, ICachingService cachingService) + { + _workflows = workflows; + _workflowStateData = workflowStateData; + _cachingService = cachingService; + + foreach (var workflow in _workflows) + { + workflow.OnChanged += Serialize; + } + + Deserialize(); + } + + public IWorkflow GetActiveWorkflow() + { + return _activeWorkflow; + } + + public void SetActiveWorkflow(IWorkflow workflow) + { + _activeWorkflow = workflow; + + OnActiveWorkflowChanged?.Invoke(_activeWorkflow); + + Serialize(); + } + + public List GetAvailableWorkflows() + { + return _workflows; + } + + private void Serialize() + { + _workflowStateData.SetActiveWorkflow(_activeWorkflow.Name); + _cachingService.CacheWorkflowStateData(_workflowStateData); + } + + private void Deserialize() + { + var serializedWorkflow = _workflowStateData.GetActiveWorkflow(); + var workflow = _workflows.FirstOrDefault(x => x.Name == serializedWorkflow); + if (workflow != null) + _activeWorkflow = workflow; + else + _activeWorkflow = _workflows[0]; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs.meta new file mode 100644 index 0000000..641626b --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f36086f9380a49949ab45463abc6fee8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs deleted file mode 100644 index ab69968..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace AssetStoreTools.Uploader.Data -{ - internal class PackageData - { - public string Id { get; } - public string Name { get; } - public string VersionId { get; } - public string Status { get; } - public string Category { get; } - public bool IsCompleteProject { get; } - public string LastUploadedPath { get; } - public string LastUploadedGuid { get; } - - public string LastDate { get; } - public string LastSize { get; } - - public PackageData(string id, string name, string versionId, string status, string category, bool isCompleteProject, string lastUploadedPath, string lastUploadedGuid, string lastDate, string lastSize) - { - Id = id; - Name = name; - VersionId = versionId; - Status = status; - Category = category; - IsCompleteProject = isCompleteProject; - LastUploadedPath = lastUploadedPath; - LastUploadedGuid = lastUploadedGuid; - LastDate = lastDate; - LastSize = lastSize; - } - - public override string ToString() - { - return $"{Id} {Name} {VersionId} {Status} {Category} {LastUploadedPath} {LastUploadedGuid} {IsCompleteProject} {LastDate} {LastSize}"; - } - } -} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs.meta deleted file mode 100644 index 2e55c38..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: 8157930875be4972a48c870a3d1e8ff1 -timeCreated: 1658919930 -AssetOrigin: - serializedVersion: 1 - productId: 115 - packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageData.cs - uploadId: 712972 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs new file mode 100644 index 0000000..78e9aa1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AssetStoreTools.Uploader.Data +{ + internal class PackageGroup : IPackageGroup + { + private class FilteredPackage + { + public IPackage Package; + public bool IsInFilter; + } + + public string Name { get; private set; } + public List Packages { get; private set; } + + private List _filteredPackages; + + public event Action> OnPackagesSorted; + public event Action> OnPackagesFiltered; + + public PackageGroup(string name, List packages) + { + Name = name; + Packages = packages; + + _filteredPackages = new List(); + foreach (var package in Packages) + _filteredPackages.Add(new FilteredPackage() { Package = package, IsInFilter = true }); + } + + public void Sort(PackageSorting sortingType) + { + switch (sortingType) + { + case PackageSorting.Name: + _filteredPackages = _filteredPackages.OrderBy(x => x.Package.Name).ToList(); + break; + case PackageSorting.Date: + _filteredPackages = _filteredPackages.OrderByDescending(x => x.Package.Modified).ToList(); + break; + case PackageSorting.Category: + _filteredPackages = _filteredPackages.OrderBy(x => x.Package.Category).ThenBy(x => x.Package.Name).ToList(); + break; + default: + throw new NotImplementedException("Undefined sorting type"); + } + + OnPackagesSorted?.Invoke(_filteredPackages.Where(x => x.IsInFilter).Select(x => x.Package).ToList()); + } + + public void Filter(string filter) + { + foreach (var package in _filteredPackages) + { + bool inFilter = package.Package.Name.ToLower().Contains(filter.ToLower()); + package.IsInFilter = inFilter; + } + + OnPackagesFiltered?.Invoke(_filteredPackages.Where(x => x.IsInFilter).Select(x => x.Package).ToList()); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs.meta similarity index 76% rename from Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs.meta rename to Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs.meta index 7508d29..a8f9623 100644 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs.meta +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 601fdada4edc5b94eb83a21d1a01ed26 +guid: c9cc17f6b95bb2c42913a1451b9af29e MonoImporter: externalObjects: {} serializedVersion: 2 @@ -13,6 +13,6 @@ AssetOrigin: serializedVersion: 1 productId: 115 packageName: Asset Store Publishing Tools - packageVersion: 11.4.4 - assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/OngoingUpload.cs - uploadId: 712972 + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageGroup.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs new file mode 100644 index 0000000..7ef00e4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs @@ -0,0 +1,9 @@ +namespace AssetStoreTools.Uploader.Data +{ + internal enum PackageSorting + { + Name, + Category, + Date + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs.meta new file mode 100644 index 0000000..de09cab --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b1d61d0de90e022469b5ed312d4b7beb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageSorting.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs deleted file mode 100644 index 367ef21..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageUploadResult.cs +++ /dev/null @@ -1,46 +0,0 @@ -using AssetStoreTools.Utility; -using UnityEngine; - -namespace AssetStoreTools.Uploader.Data -{ - internal class PackageUploadResult - { - public enum UploadStatus - { - Default = 0, - Success = 1, - Fail = 2, - Cancelled = 3, - ResponseTimeout = 4 - } - - public UploadStatus Status; - public ASError Error; - - private PackageUploadResult() { } - - public static PackageUploadResult PackageUploadSuccess() => new PackageUploadResult() { Status = UploadStatus.Success }; - - public static PackageUploadResult PackageUploadFail(ASError e) => new PackageUploadResult() { Status = UploadStatus.Fail, Error = e }; - - public static PackageUploadResult PackageUploadCancelled() => new PackageUploadResult() { Status = UploadStatus.Cancelled }; - - public static PackageUploadResult PackageUploadResponseTimeout() => new PackageUploadResult() { Status = UploadStatus.ResponseTimeout }; - - public static Color GetColorByStatus(UploadStatus status) - { - switch (status) - { - default: - case UploadStatus.Default: - return new Color(0.13f, 0.59f, 0.95f); - case UploadStatus.Success: - return new Color(0f, 0.50f, 0.14f); - case UploadStatus.Cancelled: - return new Color(0.78f, 0.59f, 0f); - case UploadStatus.Fail: - return new Color(0.69f, 0.04f, 0.04f); - } - } - } -} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization.meta new file mode 100644 index 0000000..2ba3d4c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0b05e199f21f636439844a8cc7e2c225 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs new file mode 100644 index 0000000..4970e73 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs @@ -0,0 +1,59 @@ +using AssetStoreTools.Utility; +using Newtonsoft.Json; +using System.IO; +using UnityEditor; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class AssetPath + { + [JsonProperty("path")] + private string _path = string.Empty; + [JsonProperty("guid")] + private string _guid = string.Empty; + + [JsonIgnore] + public string Path { get => _path; set { SetAssetPath(value); } } + [JsonIgnore] + public string Guid { get => _guid; set { _guid = value; } } + + public AssetPath() { } + + public AssetPath(string path) + { + SetAssetPath(path); + } + + private void SetAssetPath(string path) + { + _path = path.Replace("\\", "/"); + if (TryGetGuid(_path, out var guid)) + _guid = guid; + } + + private bool TryGetGuid(string path, out string guid) + { + guid = string.Empty; + + var relativePath = FileUtility.AbsolutePathToRelativePath(path, ASToolsPreferences.Instance.EnableSymlinkSupport); + + if (!relativePath.StartsWith("Assets/") && !relativePath.StartsWith("Packages/")) + return false; + + guid = AssetDatabase.AssetPathToGUID(relativePath); + return !string.IsNullOrEmpty(guid); + } + + public override string ToString() + { + var pathFromGuid = AssetDatabase.GUIDToAssetPath(_guid); + if (!string.IsNullOrEmpty(pathFromGuid) && (File.Exists(pathFromGuid) || Directory.Exists(pathFromGuid))) + return pathFromGuid; + + if (File.Exists(_path) || Directory.Exists(_path)) + return _path; + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs.meta new file mode 100644 index 0000000..3529bc1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 920ff8e4ffe77ec44bede985593cc187 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetPath.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs new file mode 100644 index 0000000..8d955c3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class AssetsWorkflowState + { + [JsonProperty("main_path")] + private AssetPath _mainPath; + [JsonProperty("special_folders")] + private List _specialFolders; + [JsonProperty("include_dependencies")] + private bool _includeDependencies; + [JsonProperty("dependencies")] + private List _dependencies; + + public AssetsWorkflowState() + { + _mainPath = new AssetPath(); + _includeDependencies = false; + _dependencies = new List(); + _specialFolders = new List(); + } + + public string GetMainPath() + { + return _mainPath?.ToString(); + } + + public void SetMainPath(string path) + { + _mainPath = new AssetPath(path); + } + + public bool GetIncludeDependencies() + { + return _includeDependencies; + } + + public void SetIncludeDependencies(bool value) + { + _includeDependencies = value; + } + + public List GetDependencies() + { + return _dependencies; + } + + public void SetDependencies(IEnumerable dependencies) + { + _dependencies = new List(); + foreach (var dependency in dependencies) + _dependencies.Add(dependency); + } + + public List GetSpecialFolders() + { + var specialFolders = new List(); + foreach (var folder in _specialFolders) + { + var path = folder.ToString(); + if (!string.IsNullOrEmpty(path)) + specialFolders.Add(path); + } + + return specialFolders; + } + + public void SetSpecialFolders(List specialFolders) + { + _specialFolders = new List(); + foreach (var path in specialFolders) + _specialFolders.Add(new AssetPath(path)); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs.meta new file mode 100644 index 0000000..8c7d477 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 505f0a5aa753b4445a467539e150190a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/AssetsWorkflowStateData.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs new file mode 100644 index 0000000..58e428e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class HybridPackageWorkflowState + { + [JsonProperty("package_name")] + private string _packageName; + [JsonProperty("dependencies")] + private List _dependencies; + + public HybridPackageWorkflowState() + { + _packageName = string.Empty; + _dependencies = new List(); + } + + public string GetPackageName() + { + return _packageName; + } + + public void SetPackageName(string packageName) + { + _packageName = packageName; + } + + public List GetPackageDependencies() + { + return _dependencies; + } + + public void SetPackageDependencies(IEnumerable dependencies) + { + _dependencies.Clear(); + foreach (var dependency in dependencies) + _dependencies.Add(dependency); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs.meta new file mode 100644 index 0000000..7b9f72a --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2848375fcb0a4174495573190bfc3900 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/HybridPackageWorkflowState.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs new file mode 100644 index 0000000..ca14b88 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class UnityPackageWorkflowState + { + [JsonProperty("package_path")] + private AssetPath _packagePath; + + public UnityPackageWorkflowState() + { + _packagePath = new AssetPath(); + } + + public string GetPackagePath() + { + return _packagePath?.ToString(); + } + + public void SetPackagePath(string path) + { + _packagePath = new AssetPath(path); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs.meta new file mode 100644 index 0000000..65f718e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 101a66adc88639b43b07cc28214474cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/UnityPackageWorkflowStateData.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs new file mode 100644 index 0000000..25e72c6 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace AssetStoreTools.Uploader.Data.Serialization +{ + internal class WorkflowStateData + { + [JsonProperty("package_id")] + private string _packageId; + [JsonProperty("active_workflow")] + private string _activeWorkflow; + [JsonProperty("assets_workflow")] + private AssetsWorkflowState _assetsWorkflow; + [JsonProperty("unitypackage_workflow")] + private UnityPackageWorkflowState _unityPackageWorkflow; + [JsonProperty("hybrid_workflow")] + private HybridPackageWorkflowState _hybridPackageWorkflow; + + public WorkflowStateData() + { + _activeWorkflow = string.Empty; + + _assetsWorkflow = new AssetsWorkflowState(); + _unityPackageWorkflow = new UnityPackageWorkflowState(); + _hybridPackageWorkflow = new HybridPackageWorkflowState(); + } + + public WorkflowStateData(string packageId) : this() + { + SetPackageId(packageId); + } + + public string GetPackageId() + { + return _packageId; + } + + public void SetPackageId(string packageId) + { + _packageId = packageId; + } + + public string GetActiveWorkflow() + { + return _activeWorkflow; + } + + public void SetActiveWorkflow(string activeWorkflow) + { + _activeWorkflow = activeWorkflow; + } + + public AssetsWorkflowState GetAssetsWorkflowState() + { + return _assetsWorkflow; + } + + public UnityPackageWorkflowState GetUnityPackageWorkflowState() + { + return _unityPackageWorkflow; + } + + public HybridPackageWorkflowState GetHybridPackageWorkflowState() + { + return _hybridPackageWorkflow; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs.meta new file mode 100644 index 0000000..5fb8995 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eecebbc83661a4f41a14e293c9fc3331 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Serialization/WorkflowStateData.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs new file mode 100644 index 0000000..6668fd6 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs @@ -0,0 +1,135 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Exporter; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Validator; +using AssetStoreTools.Validator.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Data +{ + internal class UnityPackageWorkflow : WorkflowBase + { + public override string Name => "UnityPackageWorkflow"; + public override string DisplayName => "Pre-exported .unitypackage"; + public override string PackageExtension => ".unitypackage"; + public override bool IsPathSet => !string.IsNullOrEmpty(_packagePath); + + private UnityPackageWorkflowState _workflowState; + private string _packagePath; + + public override event Action OnChanged; + + public UnityPackageWorkflow(IPackage package, UnityPackageWorkflowState workflowState, IWorkflowServices services) + : base(package, services) + { + _workflowState = workflowState; + Deserialize(); + } + + public void SetPackagePath(string path, bool serialize) + { + _packagePath = path; + SetMetadata(); + if (serialize) + Serialize(); + } + + private void SetMetadata() + { + LocalPackageGuid = string.Empty; + LocalPackagePath = string.Empty; + LocalProjectPath = _packagePath; + } + + public string GetPackagePath() + { + return _packagePath; + } + + public override IEnumerable GetAllPaths() + { + return new List() { _packagePath }; + } + + public override bool IsPathValid(string path, out string error) + { + error = null; + + var pathIsUnityPackage = path.EndsWith(PackageExtension); + var pathExists = File.Exists(path); + + if (!pathIsUnityPackage || !pathExists) + { + error = "Path must point to a .unitypackage file"; + return false; + } + + return true; + } + + public override IValidator CreateValidator() + { + var validationSettings = new ExternalProjectValidationSettings() + { + Category = Package.Category, + PackagePath = GetPackagePath() + }; + + var validator = new ExternalProjectValidator(validationSettings); + return validator; + } + + public override IPackageExporter CreateExporter(string _) + { + // This workflow already takes exported packages as input + throw new InvalidOperationException($"{nameof(UnityPackageWorkflow)} already takes exported packages as input"); + } + + public override Task ExportPackage(string _) + { + return Task.FromResult(new PackageExporterResult() { Success = true, ExportedPath = GetPackagePath() }); + } + + protected override IPackageUploader CreatePackageUploader(string exportedPackagePath) + { + var uploaderSettings = new UnityPackageUploadSettings() + { + VersionId = Package.VersionId, + UnityPackagePath = exportedPackagePath, + RootGuid = LocalPackageGuid, + RootPath = LocalPackagePath, + ProjectPath = LocalProjectPath + }; + + var uploader = new UnityPackageUploader(uploaderSettings); + return uploader; + } + + protected override void Serialize() + { + _workflowState.SetPackagePath(_packagePath); + OnChanged?.Invoke(); + } + + protected override void Deserialize() + { + _packagePath = _workflowState.GetPackagePath(); + DeserializeFromUploadedData(); + } + + private void DeserializeFromUploadedData() + { + if (!string.IsNullOrEmpty(_packagePath)) + return; + + var potentialPackagePath = Package.ProjectPath; + if (string.IsNullOrEmpty(potentialPackagePath) || !IsPathValid(potentialPackagePath, out var _)) + return; + + _packagePath = potentialPackagePath; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs.meta new file mode 100644 index 0000000..0179b48 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 47ee1db30792bf84aa1af8be7ce0dee6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/UnityPackageWorkflow.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs new file mode 100644 index 0000000..525374d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs @@ -0,0 +1,53 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using AssetStoreTools.Uploader.Services.Api; +using System; +using System.Threading.Tasks; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Data +{ + internal class WorkflowServices : IWorkflowServices + { + private IPackageDownloadingService _downloadingService; + private IPackageUploadingService _uploadingService; + private IAnalyticsService _analyticsService; + + public WorkflowServices( + IPackageDownloadingService downloadingService, + IPackageUploadingService uploadingService, + IAnalyticsService analyticsService) + { + _downloadingService = downloadingService; + _uploadingService = uploadingService; + _analyticsService = analyticsService; + } + + public Task GetPackageUploadedVersions(IPackage package, int timeoutMs) + { + return _downloadingService.GetPackageUploadedVersions(package, timeoutMs); + } + + public Task UploadPackage(IPackageUploader uploader, IProgress progress) + { + return _uploadingService.UploadPackage(uploader, progress); + } + + public void StopUploading(IPackageUploader uploader) + { + _uploadingService.StopUploading(uploader); + } + + public Task UpdatePackageData(IPackage package) + { + return _downloadingService.UpdatePackageData(package); + } + + public AnalyticsResult SendAnalytic(IAssetStoreAnalytic data) + { + return _analyticsService.Send(data); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs.meta new file mode 100644 index 0000000..535d449 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a78b96ae30966e94ba9ffdddf19c1692 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/WorkflowServices.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services.meta new file mode 100644 index 0000000..6d1acef --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d9787842821f3d041904186d0e0cc61d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics.meta new file mode 100644 index 0000000..30b3cbf --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 17f95678acdb51548908d81be7146b5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs new file mode 100644 index 0000000..a2f6e1e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs @@ -0,0 +1,45 @@ +using AssetStoreTools.Uploader.Services.Analytics.Data; +using UnityEditor; +using UnityEngine.Analytics; +#if !UNITY_2023_2_OR_NEWER +using AnalyticsConstants = AssetStoreTools.Constants.Uploader.Analytics; +#endif + +namespace AssetStoreTools.Uploader.Services.Analytics +{ + internal class AnalyticsService : IAnalyticsService + { + public AnalyticsResult Send(IAssetStoreAnalytic analytic) + { + if (!EditorAnalytics.enabled) + return AnalyticsResult.AnalyticsDisabled; + + if (!Register(analytic)) + return AnalyticsResult.AnalyticsDisabled; + +#if UNITY_2023_2_OR_NEWER + return EditorAnalytics.SendAnalytic(analytic); +#else + return EditorAnalytics.SendEventWithLimit(analytic.EventName, + analytic.Data, + analytic.EventVersion); +#endif + } + + private bool Register(IAssetStoreAnalytic analytic) + { +#if UNITY_2023_2_OR_NEWER + return true; +#else + var result = EditorAnalytics.RegisterEventWithLimit( + eventName: analytic.EventName, + maxEventPerHour: AnalyticsConstants.MaxEventsPerHour, + maxItems: AnalyticsConstants.MaxNumberOfElements, + vendorKey: AnalyticsConstants.VendorKey, + ver: analytic.EventVersion); + + return result == AnalyticsResult.Ok; +#endif + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs.meta new file mode 100644 index 0000000..0b230a0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 408b5b0136da9ca4f9598b8688f6210e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/AnalyticsService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data.meta new file mode 100644 index 0000000..59ec0e4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: df1fca726619f2f4fae3bd93b0ef5a8b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs new file mode 100644 index 0000000..74edf0e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs @@ -0,0 +1,46 @@ +using AssetStoreTools.Api; +using System; +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif +using AnalyticsConstants = AssetStoreTools.Constants.Uploader.Analytics; + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ +#if UNITY_2023_2_OR_NEWER + [AnalyticInfo + (eventName: AnalyticsConstants.AuthenticationAnalytics.EventName, + vendorKey: AnalyticsConstants.VendorKey, + version: AnalyticsConstants.AuthenticationAnalytics.EventVersion, + maxEventsPerHour: AnalyticsConstants.MaxEventsPerHour, + maxNumberOfElements: AnalyticsConstants.MaxNumberOfElements)] +#endif + internal class AuthenticationAnalytic : BaseAnalytic + { + [Serializable] + public class AuthenticationAnalyticData : BaseAnalyticData + { + public string AuthenticationType; + public string PublisherId; + } + + public override string EventName => AnalyticsConstants.AuthenticationAnalytics.EventName; + public override int EventVersion => AnalyticsConstants.AuthenticationAnalytics.EventVersion; + + private AuthenticationAnalyticData _data; + + public AuthenticationAnalytic(IAuthenticationType authenticationType, string publisherId) + { + _data = new AuthenticationAnalyticData + { + AuthenticationType = authenticationType.GetType().Name, + PublisherId = publisherId + }; + } + + protected override BaseAnalyticData GetData() + { + return _data; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs.meta new file mode 100644 index 0000000..b7c28ed --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4b9389e3ee578484493d36775c75baa1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/AuthenticationAnalytic.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs new file mode 100644 index 0000000..60c3396 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs @@ -0,0 +1,35 @@ +using System; +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + internal abstract class BaseAnalytic : IAssetStoreAnalytic + { + [Serializable] + public class BaseAnalyticData : IAssetStoreAnalyticData + { + public string ToolVersion = Constants.Api.ApiVersion; + } + + public abstract string EventName { get; } + public abstract int EventVersion { get; } + + public IAssetStoreAnalyticData Data => GetData(); + protected abstract BaseAnalyticData GetData(); + +#if UNITY_2023_2_OR_NEWER + public bool TryGatherData(out IAnalytic.IData data, [System.Diagnostics.CodeAnalysis.NotNullWhen(false)] out Exception error) + { + error = null; + data = Data; + + if (data == null) + error = new Exception("Analytic data is null"); + + return error == null; + } +#endif + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs.meta new file mode 100644 index 0000000..a423c7e --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 51ec1e4b6505b694ab01f7c523744fbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/BaseAnalytic.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs new file mode 100644 index 0000000..5207ca0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs @@ -0,0 +1,16 @@ +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + internal interface IAssetStoreAnalytic +#if UNITY_2023_2_OR_NEWER + : IAnalytic +#endif + { + string EventName { get; } + int EventVersion { get; } + IAssetStoreAnalyticData Data { get; } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs.meta new file mode 100644 index 0000000..18e344d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6e9b53aa176bbed48bafa538c26df304 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalytic.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs new file mode 100644 index 0000000..e08b8f8 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs @@ -0,0 +1,8 @@ +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + interface IAssetStoreAnalyticData +#if UNITY_2023_2_OR_NEWER + : UnityEngine.Analytics.IAnalytic.IData +#endif + { } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs.meta new file mode 100644 index 0000000..a79af8d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b639e25d9b9abd34d8eb67b0e17dde86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/IAssetStoreAnalyticData.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs new file mode 100644 index 0000000..d8a3479 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs @@ -0,0 +1,72 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Validator.Data; +using System; +#if UNITY_2023_2_OR_NEWER +using UnityEngine.Analytics; +#endif +using AnalyticsConstants = AssetStoreTools.Constants.Uploader.Analytics; + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ +#if UNITY_2023_2_OR_NEWER + [AnalyticInfo + (eventName: AnalyticsConstants.PackageUploadAnalytics.EventName, + vendorKey: AnalyticsConstants.VendorKey, + version: AnalyticsConstants.PackageUploadAnalytics.EventVersion, + maxEventsPerHour: AnalyticsConstants.MaxEventsPerHour, + maxNumberOfElements: AnalyticsConstants.MaxNumberOfElements)] +#endif + internal class PackageUploadAnalytic : BaseAnalytic + { + [Serializable] + public class PackageUploadAnalyticData : BaseAnalyticData + { + public string PackageId; + public string Category; + public bool UsedValidator; + public string ValidatorResults; + public string UploadFinishedReason; + public double TimeTaken; + public long PackageSize; + public string Workflow; + public string EndpointUrl; + } + + public override string EventName => AnalyticsConstants.PackageUploadAnalytics.EventName; + public override int EventVersion => AnalyticsConstants.PackageUploadAnalytics.EventVersion; + + private PackageUploadAnalyticData _data; + + public PackageUploadAnalytic( + string packageId, + string category, + bool usedValidator, + ValidationSettings validationSettings, + ValidationResult validationResult, + UploadStatus uploadFinishedReason, + double timeTaken, + long packageSize, + string workflow + ) + { + _data = new PackageUploadAnalyticData() + { + PackageId = packageId, + Category = category, + UsedValidator = usedValidator, + ValidatorResults = usedValidator ? + ValidationResultsSerializer.ConstructValidationResultsJson(validationSettings, validationResult) : null, + UploadFinishedReason = uploadFinishedReason.ToString(), + TimeTaken = timeTaken, + PackageSize = packageSize, + Workflow = workflow, + EndpointUrl = Constants.Api.AssetStoreBaseUrl + }; + } + + protected override BaseAnalyticData GetData() + { + return _data; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs.meta new file mode 100644 index 0000000..6e329eb --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6cc34de12dce9964b9c900d5bb159966 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/PackageUploadAnalytic.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs new file mode 100644 index 0000000..1bb7b56 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs @@ -0,0 +1,91 @@ +using AssetStoreTools.Validator.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System.Reflection; + +namespace AssetStoreTools.Uploader.Services.Analytics.Data +{ + internal class ValidationResultsSerializer + { + private class ValidationResults + { + public bool HasCompilationErrors; + public string[] Paths; + public Dictionary Results; + } + + private class TestResultOutcome + { + public int IntegerValue; + public string StringValue; + + public TestResultOutcome(TestResultStatus status) + { + IntegerValue = (int)status; + StringValue = status.ToString(); + } + } + + private class ValidationResultsResolver : DefaultContractResolver + { + private static ValidationResultsResolver _instance; + public static ValidationResultsResolver Instance => _instance ?? (_instance = new ValidationResultsResolver()); + + private Dictionary _propertyConversion; + + private ValidationResultsResolver() + { + _propertyConversion = new Dictionary() + { + { nameof(ValidationResults.HasCompilationErrors), "has_compilation_errors" }, + { nameof(ValidationResults.Paths), "validation_paths" }, + { nameof(ValidationResults.Results), "validation_results" }, + { nameof(TestResultOutcome.IntegerValue), "int" }, + { nameof(TestResultOutcome.StringValue), "string" }, + }; + } + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + var property = base.CreateProperty(member, memberSerialization); + if (_propertyConversion.ContainsKey(property.PropertyName)) + property.PropertyName = _propertyConversion[property.PropertyName]; + + return property; + } + } + + public static string ConstructValidationResultsJson(ValidationSettings settings, ValidationResult result) + { + if (result == null) + return string.Empty; + + var resultObject = new ValidationResults(); + resultObject.HasCompilationErrors = result.HadCompilationErrors; + + switch (settings) + { + case CurrentProjectValidationSettings currentProjectValidationSettings: + resultObject.Paths = currentProjectValidationSettings.ValidationPaths.ToArray(); + break; + case ExternalProjectValidationSettings externalProjectValidationSettings: + resultObject.Paths = new string[] { externalProjectValidationSettings.PackagePath }; + break; + } + + resultObject.Results = new Dictionary(); + foreach (var test in result.Tests) + { + resultObject.Results.Add(test.Id.ToString(), new TestResultOutcome(test.Result.Status)); + } + + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = ValidationResultsResolver.Instance + }; + + return JsonConvert.SerializeObject(resultObject, serializerSettings); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs.meta new file mode 100644 index 0000000..0dddcc2 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fa15fc27c7f3d044884885b3dad73efc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/Data/ValidationResultsSerializer.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs new file mode 100644 index 0000000..a64dce1 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs @@ -0,0 +1,10 @@ +using AssetStoreTools.Uploader.Services.Analytics.Data; +using UnityEngine.Analytics; + +namespace AssetStoreTools.Uploader.Services.Analytics +{ + internal interface IAnalyticsService : IUploaderService + { + AnalyticsResult Send(IAssetStoreAnalytic analytic); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs.meta new file mode 100644 index 0000000..2924eab --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: faa1f39fc83b86b438f6e0f34f01167b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Analytics/IAnalyticsService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api.meta new file mode 100644 index 0000000..7075aa4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d983b64bd0866a428f937434252f537 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs new file mode 100644 index 0000000..e002818 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs @@ -0,0 +1,100 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Analytics.Data; +using AssetStoreTools.Utility; +using System; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal class AuthenticationService : IAuthenticationService + { + private IAssetStoreApi _api; + private ICachingService _cachingService; + private IAnalyticsService _analyticsService; + + public User User { get; private set; } + + public AuthenticationService(IAssetStoreApi api, ICachingService cachingService, IAnalyticsService analyticsService) + { + _api = api; + _cachingService = cachingService; + _analyticsService = analyticsService; + } + + public async Task AuthenticateWithCredentials(string email, string password) + { + var authenticationType = new CredentialsAuthentication(email, password); + return await Authenticate(authenticationType); + } + + public async Task AuthenticateWithSessionToken() + { + if (!_cachingService.GetCachedSessionToken(out var cachedSessionToken)) + { + return new AuthenticationResponse() { Success = false, Exception = new Exception("No cached session token found") }; + } + + var authenticationType = new SessionAuthentication(cachedSessionToken); + return await Authenticate(authenticationType); + } + + public async Task AuthenticateWithCloudToken() + { + var authenticationType = new CloudTokenAuthentication(CloudProjectSettings.accessToken); + return await Authenticate(authenticationType); + } + + private async Task Authenticate(IAuthenticationType authenticationType) + { + var response = await _api.Authenticate(authenticationType); + HandleLoginResponse(authenticationType, response); + return response; + } + + private void HandleLoginResponse(IAuthenticationType authenticationType, AuthenticationResponse response) + { + if (!response.Success) + { + Deauthenticate(); + return; + } + + User = response.User; + _cachingService.CacheSessionToken(User.SessionId); + SendAnalytics(authenticationType, User); + } + + public bool CloudAuthenticationAvailable(out string username, out string cloudToken) + { + username = CloudProjectSettings.userName; + cloudToken = CloudProjectSettings.accessToken; + return !username.Equals("anonymous"); + } + + public void Deauthenticate() + { + _api.Deauthenticate(); + + User = null; + _cachingService.ClearCachedSessionToken(); + } + + private void SendAnalytics(IAuthenticationType authenticationType, User user) + { + try + { + // Do not send session authentication events + if (authenticationType is SessionAuthentication) + return; + + var analytic = new AuthenticationAnalytic(authenticationType, user.PublisherId); + var result = _analyticsService.Send(analytic); + } + catch (Exception e) { ASDebug.LogError($"Could not send analytics: {e}"); } + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs.meta new file mode 100644 index 0000000..8aa4983 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c1c3d6578d298d049a8dcf858fd3686e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/AuthenticationService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs new file mode 100644 index 0000000..96c9ee3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Api.Responses; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal interface IAuthenticationService : IUploaderService + { + User User { get; } + Task AuthenticateWithCredentials(string email, string password); + Task AuthenticateWithSessionToken(); + Task AuthenticateWithCloudToken(); + bool CloudAuthenticationAvailable(out string username, out string cloudToken); + void Deauthenticate(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs.meta new file mode 100644 index 0000000..ecf573f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ff0518dc0d95d3540857d138215bb900 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IAuthenticationService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs new file mode 100644 index 0000000..4cef2c0 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Data; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal interface IPackageDownloadingService : IUploaderService + { + Task GetPackageData(); + Task UpdatePackageData(IPackage package); + void ClearPackageData(); + Task GetPackageThumbnail(IPackage package); + Task GetPackageUploadedVersions(IPackage package, int timeoutMs); + void StopDownloading(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs.meta new file mode 100644 index 0000000..0471c59 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 96acd12a628311d429cc285f418f8b90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageDownloadingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs new file mode 100644 index 0000000..f1ff1d4 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs @@ -0,0 +1,16 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using System; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal interface IPackageUploadingService : IUploaderService + { + bool IsUploading { get; } + + Task UploadPackage(IPackageUploader uploader, IProgress progress); + void StopUploading(IPackageUploader package); + void StopAllUploadinng(); + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs.meta new file mode 100644 index 0000000..b3fd437 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9a3d78f3bc68d3d44b4300bc8ffe69c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/IPackageUploadingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs new file mode 100644 index 0000000..5ab052f --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs @@ -0,0 +1,109 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using AssetStoreTools.Uploader.Data; +using System.Threading; +using System.Threading.Tasks; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal class PackageDownloadingService : IPackageDownloadingService + { + public const int MaxConcurrentTumbnailDownloads = 10; + + private IAssetStoreApi _api; + private ICachingService _cachingService; + + private int _currentDownloads; + private CancellationTokenSource _cancellationTokenSource; + + public PackageDownloadingService(IAssetStoreApi api, ICachingService cachingService) + { + _api = api; + _cachingService = cachingService; + _cancellationTokenSource = new CancellationTokenSource(); + } + + public void ClearPackageData() + { + _cachingService.DeletePackageMetadata(); + } + + public async Task GetPackageData() + { + if (!_cachingService.GetCachedPackageMetadata(out var models)) + { + var cancellationToken = _cancellationTokenSource.Token; + var packagesResponse = await _api.GetPackages(cancellationToken); + + if (packagesResponse.Cancelled || !packagesResponse.Success) + return packagesResponse; + + _cachingService.CachePackageMetadata(packagesResponse.Packages); + return packagesResponse; + } + + return new PackagesDataResponse() { Success = true, Packages = models }; + } + + public async Task UpdatePackageData(IPackage package) + { + var response = await _api.RefreshPackageMetadata(package.ToModel()); + + if (response.Success) + _cachingService.UpdatePackageMetadata(response.Package); + + return response; + } + + public async Task GetPackageThumbnail(IPackage package) + { + if (_cachingService.GetCachedPackageThumbnail(package.PackageId, out var cachedTexture)) + { + return new PackageThumbnailResponse() { Success = true, Thumbnail = cachedTexture }; + } + + var cancellationToken = _cancellationTokenSource.Token; + while (_currentDownloads >= MaxConcurrentTumbnailDownloads) + await Task.Delay(100); + + if (cancellationToken.IsCancellationRequested) + return new PackageThumbnailResponse() { Success = false, Cancelled = true }; + + _currentDownloads++; + var result = await _api.GetPackageThumbnail(package.ToModel(), cancellationToken); + _currentDownloads--; + + if (result.Success && result.Thumbnail != null) + _cachingService.CachePackageThumbnail(package.PackageId, result.Thumbnail); + + return result; + } + + public async Task GetPackageUploadedVersions(IPackage package, int timeoutMs) + { + var timeoutTokenSource = new CancellationTokenSource(); + try + { + var versionsTask = _api.GetPackageUploadedVersions(package.ToModel(), timeoutTokenSource.Token); + + // Wait for versions to be retrieved, or a timeout to occur, whichever is first + if (await Task.WhenAny(versionsTask, Task.Delay(timeoutMs)) != versionsTask) + { + timeoutTokenSource.Cancel(); + } + + return await versionsTask; + } + finally + { + timeoutTokenSource.Dispose(); + } + } + + public void StopDownloading() + { + _cancellationTokenSource.Cancel(); + _cancellationTokenSource = new CancellationTokenSource(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs.meta new file mode 100644 index 0000000..7ff441d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: adc44e974cb91b54fac3819284b7ba82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageDownloadingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs new file mode 100644 index 0000000..53da1bb --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs @@ -0,0 +1,103 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Api.Responses; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using UnityEditor; + +namespace AssetStoreTools.Uploader.Services.Api +{ + internal class PackageUploadingService : IPackageUploadingService + { + private class UploadInProgress + { + public IPackageUploader Uploader; + public IProgress Progress = new Progress(); + public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); + + public UploadInProgress(IPackageUploader uploader, IProgress progress) + { + Uploader = uploader; + Progress = progress; + CancellationTokenSource = new CancellationTokenSource(); + } + } + + private IAssetStoreApi _api; + private List _uploadsInProgress; + + public bool IsUploading => _uploadsInProgress.Count > 0; + + public PackageUploadingService(IAssetStoreApi api) + { + _api = api; + _uploadsInProgress = new List(); + } + + public async Task UploadPackage(IPackageUploader uploader, IProgress progress) + { + using (var cancellationTokenSource = new CancellationTokenSource()) + { + var uploadInProgress = StartTrackingUpload(uploader, progress); + var response = await _api.UploadPackage(uploadInProgress.Uploader, uploadInProgress.Progress, uploadInProgress.CancellationTokenSource.Token); + StopTrackingUpload(uploadInProgress); + + return response; + } + } + + private UploadInProgress StartTrackingUpload(IPackageUploader uploader, IProgress progress) + { + // If this is the first upload - lock reload assemblies and prevent entering play mode + if (_uploadsInProgress.Count == 0) + { + EditorApplication.LockReloadAssemblies(); + EditorApplication.playModeStateChanged += PreventEnteringPlayMode; + } + + var uploadInProgress = new UploadInProgress(uploader, progress); + _uploadsInProgress.Add(uploadInProgress); + + return uploadInProgress; + } + + private void StopTrackingUpload(UploadInProgress uploadInProgress) + { + _uploadsInProgress.Remove(uploadInProgress); + + // If this was the last upload - unlock reload assemblies and allow entering play mode + if (_uploadsInProgress.Count > 0) + return; + + EditorApplication.UnlockReloadAssemblies(); + EditorApplication.playModeStateChanged -= PreventEnteringPlayMode; + } + + private void PreventEnteringPlayMode(PlayModeStateChange change) + { + if (change != PlayModeStateChange.ExitingEditMode) + return; + + EditorApplication.ExitPlaymode(); + EditorUtility.DisplayDialog("Notice", "Entering Play Mode is not allowed while there's a package upload in progress.\n\n" + + "Please wait until the upload is finished or cancel the upload from the Asset Store Uploader window", "OK"); + } + + public void StopUploading(IPackageUploader uploader) + { + var uploadInProgress = _uploadsInProgress.FirstOrDefault(x => x.Uploader == uploader); + if (uploadInProgress == null) + return; + + uploadInProgress.CancellationTokenSource.Cancel(); + } + + public void StopAllUploadinng() + { + foreach (var uploadInProgress in _uploadsInProgress) + uploadInProgress.CancellationTokenSource.Cancel(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs.meta new file mode 100644 index 0000000..54f5def --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 22e23997fe339a74bb5355d6a88ce731 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Api/PackageUploadingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching.meta new file mode 100644 index 0000000..7267ddb --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a834946d92154754493879c5fcc7dbc9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs new file mode 100644 index 0000000..c86ee80 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs @@ -0,0 +1,157 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Utility; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.Services +{ + internal class CachingService : ICachingService + { + private VisualElement _cachedUploaderWindow; + + public bool GetCachedUploaderWindow(out VisualElement uploaderWindow) + { + uploaderWindow = _cachedUploaderWindow; + return uploaderWindow != null; + } + + public void CacheUploaderWindow(VisualElement uploaderWindow) + { + _cachedUploaderWindow = uploaderWindow; + } + + public void CacheSessionToken(string sessionToken) + { + if (string.IsNullOrEmpty(sessionToken)) + throw new ArgumentException("Session token cannot be null"); + + EditorPrefs.SetString(Constants.Cache.SessionTokenKey, sessionToken); + } + + public bool GetCachedSessionToken(out string sessionToken) + { + sessionToken = EditorPrefs.GetString(Constants.Cache.SessionTokenKey, string.Empty); + return !string.IsNullOrEmpty(sessionToken); + } + + public void ClearCachedSessionToken() + { + EditorPrefs.DeleteKey(Constants.Cache.SessionTokenKey); + } + + public bool GetCachedPackageMetadata(out List data) + { + data = new List(); + if (!CacheUtil.GetFileFromTempCache(Constants.Cache.PackageDataFileName, out var filePath)) + return false; + + try + { + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = Package.CachedPackageResolver.Instance + }; + + data = JsonConvert.DeserializeObject>(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings); + return true; + } + catch + { + return false; + } + } + + public void CachePackageMetadata(List data) + { + if (data == null) + throw new ArgumentException("Package data cannot be null"); + + var serializerSettings = new JsonSerializerSettings() + { + ContractResolver = Package.CachedPackageResolver.Instance, + Formatting = Formatting.Indented + }; + + CacheUtil.CreateFileInTempCache(Constants.Cache.PackageDataFileName, JsonConvert.SerializeObject(data, serializerSettings), true); + } + + public void DeletePackageMetadata() + { + CacheUtil.DeleteFileFromTempCache(Constants.Cache.PackageDataFileName); + } + + public void UpdatePackageMetadata(Package data) + { + if (!GetCachedPackageMetadata(out var cachedData)) + return; + + var index = cachedData.FindIndex(x => x.PackageId.Equals(data.PackageId)); + if (index == -1) + { + cachedData.Add(data); + } + else + { + cachedData.RemoveAt(index); + cachedData.Insert(index, data); + } + + CachePackageMetadata(cachedData); + } + + public bool GetCachedPackageThumbnail(string packageId, out Texture2D texture) + { + texture = null; + if (!CacheUtil.GetFileFromTempCache(Constants.Cache.PackageThumbnailFileName(packageId), out var filePath)) + return false; + + texture = new Texture2D(1, 1); + texture.LoadImage(File.ReadAllBytes(filePath)); + return true; + } + + public void CachePackageThumbnail(string packageId, Texture2D texture) + { + CacheUtil.CreateFileInTempCache(Constants.Cache.PackageThumbnailFileName(packageId), texture.EncodeToPNG(), true); + } + + public bool GetCachedWorkflowStateData(string packageId, out WorkflowStateData data) + { + data = null; + + if (string.IsNullOrEmpty(packageId)) + return false; + + if (!CacheUtil.GetFileFromPersistentCache(Constants.Cache.WorkflowStateDataFileName(packageId), out var filePath)) + return false; + + try + { + data = JsonConvert.DeserializeObject(File.ReadAllText(filePath, Encoding.UTF8)); + if (string.IsNullOrEmpty(data.GetPackageId())) + return false; + } + catch + { + return false; + } + + return true; + } + + public void CacheWorkflowStateData(WorkflowStateData data) + { + if (data == null) + throw new ArgumentException("Workflow state data cannot be null"); + + CacheUtil.CreateFileInPersistentCache(Constants.Cache.WorkflowStateDataFileName(data.GetPackageId()), JsonConvert.SerializeObject(data, Formatting.Indented), true); + } + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs.meta new file mode 100644 index 0000000..0999689 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fffaed09a3f76f945a7ececfb355f3e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/CachingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs new file mode 100644 index 0000000..1077281 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs @@ -0,0 +1,25 @@ +using AssetStoreTools.Api.Models; +using AssetStoreTools.Uploader.Data.Serialization; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; + +namespace AssetStoreTools.Uploader.Services +{ + internal interface ICachingService : IUploaderService + { + void CacheUploaderWindow(VisualElement uploaderWindow); + bool GetCachedUploaderWindow(out VisualElement uploaderWindow); + void CacheSessionToken(string sessionToken); + bool GetCachedSessionToken(out string sessionToken); + void ClearCachedSessionToken(); + bool GetCachedPackageMetadata(out List data); + void UpdatePackageMetadata(Package data); + void CachePackageMetadata(List data); + void DeletePackageMetadata(); + bool GetCachedPackageThumbnail(string packageId, out Texture2D texture); + void CachePackageThumbnail(string packageId, Texture2D texture); + bool GetCachedWorkflowStateData(string packageId, out WorkflowStateData data); + void CacheWorkflowStateData(WorkflowStateData data); + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs.meta new file mode 100644 index 0000000..2af6a1d --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a904477679e07bc4889bc15e894c0c48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/Caching/ICachingService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs new file mode 100644 index 0000000..424dd9c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs @@ -0,0 +1,4 @@ +namespace AssetStoreTools.Uploader.Services +{ + internal interface IUploaderService { } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs.meta new file mode 100644 index 0000000..ee2c2d3 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 757d7a4dc29863740859c936be514fea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/IUploaderService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory.meta new file mode 100644 index 0000000..cb9e723 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 02e4a5ee9e2fb7941b876b207078e01d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs new file mode 100644 index 0000000..83c9c48 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs @@ -0,0 +1,18 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Data.Serialization; +using System.Collections.Generic; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Services +{ + internal interface IPackageFactoryService : IUploaderService + { + IPackageGroup CreatePackageGroup(string groupName, List packages); + IPackage CreatePackage(PackageModel packageModel); + IPackageContent CreatePackageContent(IPackage package); + List CreateWorkflows(IPackage package, WorkflowStateData stateData); + AssetsWorkflow CreateAssetsWorkflow(IPackage package, AssetsWorkflowState stateData); + UnityPackageWorkflow CreateUnityPackageWorkflow(IPackage package, UnityPackageWorkflowState stateData); + HybridPackageWorkflow CreateHybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData); + } +} diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs.meta new file mode 100644 index 0000000..e108450 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 14324b71768a1ea499baa06de33f05af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/IPackageFactoryService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs new file mode 100644 index 0000000..a9fffa5 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs @@ -0,0 +1,95 @@ +using AssetStoreTools.Uploader.Data; +using AssetStoreTools.Uploader.Data.Serialization; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Api; +using System.Collections.Generic; +using PackageModel = AssetStoreTools.Api.Models.Package; + +namespace AssetStoreTools.Uploader.Services +{ + internal class PackageFactoryService : IPackageFactoryService + { + private IWorkflowServices _workflowServices; + + // Service dependencies + private ICachingService _cachingService; + private IPackageDownloadingService _packageDownloadingService; + private IPackageUploadingService _packageUploadingService; + private IAnalyticsService _analyticsService; + + public PackageFactoryService( + ICachingService cachingService, + IPackageDownloadingService packageDownloadingService, + IPackageUploadingService packageUploadingService, + IAnalyticsService analyticsService + ) + { + _cachingService = cachingService; + _packageDownloadingService = packageDownloadingService; + _packageUploadingService = packageUploadingService; + _analyticsService = analyticsService; + + _workflowServices = new WorkflowServices(_packageDownloadingService, _packageUploadingService, _analyticsService); + } + + public IPackage CreatePackage(PackageModel packageModel) + { + var package = new Package(packageModel); + return package; + } + + public IPackageGroup CreatePackageGroup(string groupName, List packages) + { + return new PackageGroup(groupName, packages); + } + + public IPackageContent CreatePackageContent(IPackage package) + { + if (!package.IsDraft) + return null; + + WorkflowStateData stateData = GetOrCreateWorkflowStateData(package); + + var workflows = CreateWorkflows(package, stateData); + var packageContent = new PackageContent(workflows, stateData, _cachingService); + return packageContent; + } + + public List CreateWorkflows(IPackage package, WorkflowStateData stateData) + { + var workflows = new List + { + CreateAssetsWorkflow(package, stateData.GetAssetsWorkflowState()), + CreateUnityPackageWorkflow(package, stateData.GetUnityPackageWorkflowState()), +#if UNITY_ASTOOLS_EXPERIMENTAL + CreateHybridPackageWorkflow(package, stateData.GetHybridPackageWorkflowState()), +#endif + }; + + return workflows; + } + + public AssetsWorkflow CreateAssetsWorkflow(IPackage package, AssetsWorkflowState stateData) + { + return new AssetsWorkflow(package, stateData, _workflowServices); + } + + public UnityPackageWorkflow CreateUnityPackageWorkflow(IPackage package, UnityPackageWorkflowState stateData) + { + return new UnityPackageWorkflow(package, stateData, _workflowServices); + } + + public HybridPackageWorkflow CreateHybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData) + { + return new HybridPackageWorkflow(package, stateData, _workflowServices); + } + + private WorkflowStateData GetOrCreateWorkflowStateData(IPackage package) + { + if (!_cachingService.GetCachedWorkflowStateData(package.PackageId, out var stateData)) + stateData = new WorkflowStateData(package.PackageId); + + return stateData; + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs.meta new file mode 100644 index 0000000..86dfc2c --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4074a5b21b6201d449974dcfb652a00b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/PackageFactory/PackageFactoryService.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs new file mode 100644 index 0000000..0aaa686 --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs @@ -0,0 +1,26 @@ +using AssetStoreTools.Api; +using AssetStoreTools.Uploader.Services.Analytics; +using AssetStoreTools.Uploader.Services.Api; +using AssetStoreTools.Utility; + +namespace AssetStoreTools.Uploader.Services +{ + internal class UploaderServiceProvider : ServiceProvider + { + public static UploaderServiceProvider Instance => _instance ?? (_instance = new UploaderServiceProvider()); + private static UploaderServiceProvider _instance; + + private UploaderServiceProvider() { } + + protected override void RegisterServices() + { + var api = new AssetStoreApi(new AssetStoreClient()); + Register(); + Register(); + Register(() => new AuthenticationService(api, GetService(), GetService())); + Register(() => new PackageDownloadingService(api, GetService())); + Register(() => new PackageUploadingService(api)); + Register(); + } + } +} \ No newline at end of file diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs.meta b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs.meta new file mode 100644 index 0000000..298a1cf --- /dev/null +++ b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e66f9c7f198baff41ba77f4d0ed7b60f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 115 + packageName: Asset Store Publishing Tools + packageVersion: 12.0.1 + assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Services/UploaderServiceProvider.cs + uploadId: 724584 diff --git a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login/LoginWindow.cs b/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login/LoginWindow.cs deleted file mode 100644 index 8662e03..0000000 --- a/Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/UI Elements/Login/LoginWindow.cs +++ /dev/null @@ -1,238 +0,0 @@ -using AssetStoreTools.Utility; -using AssetStoreTools.Utility.Json; -using System; -using UnityEditor; -using UnityEngine; -using UnityEngine.UIElements; - -namespace AssetStoreTools.Uploader.UIElements -{ -#if UNITY_6000_0_OR_NEWER - [UxmlElement] -#endif - internal partial class LoginWindow : VisualElement - { - -#if !UNITY_6000_0_OR_NEWER - public new class UxmlFactory : UxmlFactory { } -#endif - - private readonly string REGISTER_URL = "https://publisher.unity.com/access"; - private readonly string FORGOT_PASSWORD_URL = "https://id.unity.com/password/new"; - - private Button _cloudLoginButton; - private Button _credentialsLoginButton; - - private Label _cloudLoginLabel; - - private TextField _emailField; - private TextField _passwordField; - - private Box _errorBox; - private Label _errorLabel; - - private double _cloudLoginRefreshTime = 1d; - private double _lastRefreshTime; - - public LoginWindow() - { - styleSheets.Add(StyleSelector.UploaderWindow.LoginWindowStyle); - styleSheets.Add(StyleSelector.UploaderWindow.LoginWindowTheme); - ConstructLoginWindow(); - EditorApplication.update += UpdateCloudLoginButton; - } - - public void SetupLoginElements(Action onSuccess, Action onFail) - { - this.SetEnabled(true); - - _cloudLoginLabel = _cloudLoginButton.Q