update libs

This commit is contained in:
Kirill Chikalin
2025-02-13 17:48:12 +03:00
parent e17e7c2786
commit 275dc598c7
816 changed files with 22479 additions and 10792 deletions

View File

@@ -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
{
/// <summary>
/// A class for retrieving data from the Asset Store backend <para/>
/// <b>Note:</b> most data retrieval methods require <see cref="SavedSessionId"/> to be set
/// </summary>
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<string, OngoingUpload> ActiveUploads = new ConcurrentDictionary<string, OngoingUpload>();
public static bool IsUploading => (ActiveUploads.Count > 0);
static AssetStoreAPI()
{
ServicePointManager.DefaultConnectionLimit = 500;
httpClient.DefaultRequestHeaders.ConnectionClose = false;
httpClient.Timeout = TimeSpan.FromMinutes(1320);
}
/// <summary>
/// A structure used to return the success outcome and the result of Asset Store API calls
/// </summary>
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
/// <summary>
/// A login API call that uses the email and password credentials
/// </summary>
/// <remarks>
/// <b>Note:</b> this method only returns a response from the server and does not set the <see cref="SavedSessionId"/> itself
/// </remarks>
public static async Task<APIResult> LoginWithCredentialsAsync(string email, string password)
{
FormUrlEncodedContent data = GetLoginContent(new Dictionary<string, string> { { "user", email }, { "pass", password } });
return await LoginAsync(data);
}
/// <summary>
/// A login API call that uses the <see cref="SavedSessionId"/>
/// </summary>
/// <remarks>
/// <b>Note:</b> this method only returns a response from the server and does not set the <see cref="SavedSessionId"/> itself
/// </remarks>
public static async Task<APIResult> 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<string, string> { { "reuse_session", SavedSessionId }, { "xunitysession", UnauthSessionId } });
return await LoginAsync(data);
}
/// <summary>
/// A login API call that uses the <see cref="CloudProjectSettings.accessToken"/><para/>
/// </summary>
/// <remarks>
/// <b>Note:</b> this method only returns a response from the server and does not set the <see cref="SavedSessionId"/> itself
/// </remarks>
/// <param name="token">Cloud access token. Can be retrieved by calling <see cref="CloudProjectSettings.accessToken"/></param>
public static async Task<APIResult> LoginWithTokenAsync(string token)
{
FormUrlEncodedContent data = GetLoginContent(new Dictionary<string, string> { { "user_access_token", token } });
return await LoginAsync(data);
}
private static async Task<APIResult> 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<JsonValue> GetPackageDataMain()
{
return await GetAssetStoreData(APIUri("asset-store-tools", "metadata/0", SavedSessionId));
}
private static async Task<JsonValue> GetPackageDataExtra()
{
return await GetAssetStoreData(APIUri("management", "packages", SavedSessionId));
}
private static async Task<JsonValue> 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;
}
/// <summary>
/// Retrieve data for all packages associated with the currently logged in account (identified by <see cref="SavedSessionId"/>)
/// </summary>
/// <param name="useCached"></param>
/// <returns></returns>
public static async Task<APIResult> 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) };
}
}
/// <summary>
/// Retrieve the thumbnail textures for all packages within the provided json structure and perform a given action after each retrieval
/// </summary>
/// <param name="packageJson">A json file retrieved from <see cref="GetFullPackageDataAsync(bool)"/></param>
/// <param name="useCached">Return cached thumbnails if they are found</param>
/// <param name="onSuccess">
/// Action to perform upon a successful thumbnail retrieval <para/>
/// <see cref="string"/> - Package Id <br/>
/// <see cref="Texture2D"/> - Associated Thumbnail
/// </param>
/// <param name="onFail">
/// Action to perform upon a failed thumbnail retrieval <para/>
/// <see cref="string"/> - Package Id <br/>
/// <see cref="ASError"/> - Associated error
/// </param>
public static async void GetPackageThumbnails(JsonValue packageJson, bool useCached, Action<string, Texture2D> onSuccess, Action<string, ASError> 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<byte[]> 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;
}
/// <summary>
/// Retrieve, update the cache and return the updated data for a previously cached package
/// </summary>
public static async Task<APIResult> 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) };
}
}
/// <summary>
/// Retrieve all Unity versions that the given package has already had uploaded content with
/// </summary>
/// <param name="packageId"></param>
/// <param name="versionId"></param>
/// <returns></returns>
public static List<string> GetPackageUploadedVersions(string packageId, string versionId)
{
var versions = new List<string>();
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
/// <summary>
/// Upload a content file (.unitypackage) to a provided package version
/// </summary>
/// <param name="versionId"></param>
/// <param name="packageName">Name of the package. Only used for identifying the package in <see cref="OngoingUpload"/> class</param>
/// <param name="filePath">Path to the .unitypackage file</param>
/// <param name="localPackageGuid">The <see cref="AssetDatabase.AssetPathToGUID(string)"/> value of the main content folder for the provided package</param>
/// <param name="localPackagePath">The local path (relative to the root project folder) of the main content folder for the provided package</param>
/// <param name="localProjectPath">The path to the project that this package was built from</param>
/// <returns></returns>
public static async Task<PackageUploadResult> 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<string, string> packageParams = new Dictionary<string, string>
{
// 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();
}
}
/// <summary>
/// Cancel the uploading task for a package with the provided package id
/// </summary>
public static void AbortPackageUpload(string packageId)
{
ActiveUploads[packageId]?.Cancel();
}
#endregion
#region Utility Methods
public static async Task<APIResult> 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<string, string> 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<JsonValue> 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<string, string> extraQuery)
{
Dictionary<string, string> extraQueryMerged;
if (extraQuery == null)
extraQueryMerged = new Dictionary<string, string>();
else
extraQueryMerged = new Dictionary<string, string>(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<string, string> 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<string, string> CreateCategoryDictionary(JsonValue json)
{
var categories = new Dictionary<string, string>();
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;
}
/// <summary>
/// Check if the account data is for a valid publisher account
/// </summary>
/// <param name="json">Json structure retrieved from one of the API login methods</param>
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();
}
/// <summary>
/// Cancel all data retrieval tasks
/// </summary>
public static void AbortDownloadTasks()
{
s_downloadCancellationSource?.Cancel();
}
/// <summary>
/// Cancel all data uploading tasks
/// </summary>
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
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9e3cae7082463da41b807724242fd617
guid: 930cfc857321b1048bf9607d4c8f89d3
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 3eb6991a3db8cc34dad63504bc6c3c0e
guid: 771776e4d51c47945b3449d4de948c00
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace AssetStoreTools.Uploader.Data
{
internal interface IPackageContent
{
event Action<IWorkflow> OnActiveWorkflowChanged;
IWorkflow GetActiveWorkflow();
List<IWorkflow> GetAvailableWorkflows();
void SetActiveWorkflow(IWorkflow workflow);
}
}

View File

@@ -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

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace AssetStoreTools.Uploader.Data
{
internal interface IPackageGroup
{
string Name { get; }
List<IPackage> Packages { get; }
event Action<List<IPackage>> OnPackagesSorted;
event Action<List<IPackage>> OnPackagesFiltered;
void Sort(PackageSorting sortingType);
void Filter(string filter);
}
}

View File

@@ -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

View File

@@ -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<UploadStatus?, float?> OnUploadStateChanged;
bool GenerateHighQualityPreviews { get; set; }
ValidationSettings LastValidationSettings { get; }
ValidationResult LastValidationResult { get; }
IEnumerable<string> GetAllPaths();
ValidationResult Validate();
Task<PackageExporterResult> ExportPackage(string outputPath);
Task<bool> ValidatePackageUploadedVersions();
Task<PackageUploadResponse> UploadPackage(string exportedPackagePath);
void AbortUpload();
void ResetUploadStatus();
Task RefreshPackage();
}
}

View File

@@ -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

View File

@@ -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<PackageUploadedUnityVersionDataResponse> GetPackageUploadedVersions(IPackage package, int timeoutMs);
Task<PackageUploadResponse> UploadPackage(IPackageUploader uploader, IProgress<float> progress);
void StopUploading(IPackageUploader uploader);
AnalyticsResult SendAnalytic(IAssetStoreAnalytic data);
Task<RefreshedPackageDataResponse> UpdatePackageData(IPackage package);
}
}

View File

@@ -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

View File

@@ -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<UploadStatus?, float?> OnUploadStateChanged;
public WorkflowBase(IPackage package, IWorkflowServices services)
{
Package = package;
_services = services;
}
public abstract IEnumerable<string> 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<string> 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<PackageExporterResult> ExportPackage(string outputPath)
{
var exporter = CreateExporter(outputPath);
var result = await exporter.Export();
return result;
}
public async Task<bool> 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<PackageUploadResponse> 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<float>();
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();
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d4a93170d5bda304895e5feaf6e34aa8
guid: d0e87ee17aa944c42b1c335abe19daaf
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/Login/LoginWindow.cs
uploadId: 712972
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/Abstractions/WorkflowBase.cs
uploadId: 724584

View File

@@ -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<PackageInfo> _dependencies;
private List<string> _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<PackageInfo> GetDependencies()
{
return _dependencies;
}
public void SetDependencies(IEnumerable<string> 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<string> GetSpecialFolders()
{
return _specialFolders;
}
public void SetSpecialFolders(IEnumerable<string> 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<string> GetAvailableDependencies()
{
var registryPackages = PackageUtility.GetAllRegistryPackages();
return registryPackages.Select(x => x.name).ToList();
}
public List<string> GetAvailableSpecialFolders()
{
var specialFolders = new List<string>();
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<string> GetAllPaths()
{
var paths = new List<string>()
{
_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<string>();
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<string>();
foreach (var path in _stateData.GetSpecialFolders())
{
_specialFolders.Add(path);
}
_includeDependencies = _stateData.GetIncludeDependencies();
_dependencies = new List<PackageInfo>();
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;
}
}
}

View File

@@ -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

View File

@@ -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<PackageInfo> _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<PackageInfo> GetDependencies()
{
return _dependencies;
}
public void SetDependencies(IEnumerable<string> 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<PackageInfo> GetAvailableDependencies()
{
var availableDependencies = new List<PackageInfo>();
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<string> GetAllPaths()
{
var paths = new List<string>();
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<PackageInfo>();
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;
}
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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<IWorkflow> _workflows;
private WorkflowStateData _workflowStateData;
private ICachingService _cachingService;
public event Action<IWorkflow> OnActiveWorkflowChanged;
public PackageContent(List<IWorkflow> 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<IWorkflow> 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];
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 188361b01a1450145a6fc2a7aa0a3a3c
guid: f36086f9380a49949ab45463abc6fee8
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/PackageUploadResult.cs
uploadId: 712972
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Uploader/Scripts/Data/PackageContent.cs
uploadId: 724584

View File

@@ -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}";
}
}
}

View File

@@ -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

View File

@@ -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<IPackage> Packages { get; private set; }
private List<FilteredPackage> _filteredPackages;
public event Action<List<IPackage>> OnPackagesSorted;
public event Action<List<IPackage>> OnPackagesFiltered;
public PackageGroup(string name, List<IPackage> packages)
{
Name = name;
Packages = packages;
_filteredPackages = new List<FilteredPackage>();
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());
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,9 @@
namespace AssetStoreTools.Uploader.Data
{
internal enum PackageSorting
{
Name,
Category,
Date
}
}

View File

@@ -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

View File

@@ -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);
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d6e2d6bcfe000764e9330d78017e32bc
guid: 0b05e199f21f636439844a8cc7e2c225
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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<AssetPath> _specialFolders;
[JsonProperty("include_dependencies")]
private bool _includeDependencies;
[JsonProperty("dependencies")]
private List<string> _dependencies;
public AssetsWorkflowState()
{
_mainPath = new AssetPath();
_includeDependencies = false;
_dependencies = new List<string>();
_specialFolders = new List<AssetPath>();
}
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<string> GetDependencies()
{
return _dependencies;
}
public void SetDependencies(IEnumerable<string> dependencies)
{
_dependencies = new List<string>();
foreach (var dependency in dependencies)
_dependencies.Add(dependency);
}
public List<string> GetSpecialFolders()
{
var specialFolders = new List<string>();
foreach (var folder in _specialFolders)
{
var path = folder.ToString();
if (!string.IsNullOrEmpty(path))
specialFolders.Add(path);
}
return specialFolders;
}
public void SetSpecialFolders(List<string> specialFolders)
{
_specialFolders = new List<AssetPath>();
foreach (var path in specialFolders)
_specialFolders.Add(new AssetPath(path));
}
}
}

View File

@@ -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

View File

@@ -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<string> _dependencies;
public HybridPackageWorkflowState()
{
_packageName = string.Empty;
_dependencies = new List<string>();
}
public string GetPackageName()
{
return _packageName;
}
public void SetPackageName(string packageName)
{
_packageName = packageName;
}
public List<string> GetPackageDependencies()
{
return _dependencies;
}
public void SetPackageDependencies(IEnumerable<string> dependencies)
{
_dependencies.Clear();
foreach (var dependency in dependencies)
_dependencies.Add(dependency);
}
}
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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<string> GetAllPaths()
{
return new List<string>() { _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<PackageExporterResult> 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;
}
}
}

View File

@@ -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

View File

@@ -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<PackageUploadedUnityVersionDataResponse> GetPackageUploadedVersions(IPackage package, int timeoutMs)
{
return _downloadingService.GetPackageUploadedVersions(package, timeoutMs);
}
public Task<PackageUploadResponse> UploadPackage(IPackageUploader uploader, IProgress<float> progress)
{
return _uploadingService.UploadPackage(uploader, progress);
}
public void StopUploading(IPackageUploader uploader)
{
_uploadingService.StopUploading(uploader);
}
public Task<RefreshedPackageDataResponse> UpdatePackageData(IPackage package)
{
return _downloadingService.UpdatePackageData(package);
}
public AnalyticsResult SendAnalytic(IAssetStoreAnalytic data)
{
return _analyticsService.Send(data);
}
}
}

View File

@@ -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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 854f6f9e93b37204eb2e6042138643bc
guid: d9787842821f3d041904186d0e0cc61d
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7562bae25c6218744a023670e3a2f06f
guid: 17f95678acdb51548908d81be7146b5b
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: df1fca726619f2f4fae3bd93b0ef5a8b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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; }
}
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
namespace AssetStoreTools.Uploader.Services.Analytics.Data
{
interface IAssetStoreAnalyticData
#if UNITY_2023_2_OR_NEWER
: UnityEngine.Analytics.IAnalytic.IData
#endif
{ }
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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<string, TestResultOutcome> 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<string, string> _propertyConversion;
private ValidationResultsResolver()
{
_propertyConversion = new Dictionary<string, string>()
{
{ 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<string, TestResultOutcome>();
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);
}
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4d983b64bd0866a428f937434252f537
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<AuthenticationResponse> AuthenticateWithCredentials(string email, string password)
{
var authenticationType = new CredentialsAuthentication(email, password);
return await Authenticate(authenticationType);
}
public async Task<AuthenticationResponse> 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<AuthenticationResponse> AuthenticateWithCloudToken()
{
var authenticationType = new CloudTokenAuthentication(CloudProjectSettings.accessToken);
return await Authenticate(authenticationType);
}
private async Task<AuthenticationResponse> 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}"); }
}
}
}

View File

@@ -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

View File

@@ -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<AuthenticationResponse> AuthenticateWithCredentials(string email, string password);
Task<AuthenticationResponse> AuthenticateWithSessionToken();
Task<AuthenticationResponse> AuthenticateWithCloudToken();
bool CloudAuthenticationAvailable(out string username, out string cloudToken);
void Deauthenticate();
}
}

View File

@@ -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

View File

@@ -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<PackagesDataResponse> GetPackageData();
Task<RefreshedPackageDataResponse> UpdatePackageData(IPackage package);
void ClearPackageData();
Task<PackageThumbnailResponse> GetPackageThumbnail(IPackage package);
Task<PackageUploadedUnityVersionDataResponse> GetPackageUploadedVersions(IPackage package, int timeoutMs);
void StopDownloading();
}
}

View File

@@ -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

View File

@@ -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<PackageUploadResponse> UploadPackage(IPackageUploader uploader, IProgress<float> progress);
void StopUploading(IPackageUploader package);
void StopAllUploadinng();
}
}

View File

@@ -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

View File

@@ -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<PackagesDataResponse> 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<RefreshedPackageDataResponse> UpdatePackageData(IPackage package)
{
var response = await _api.RefreshPackageMetadata(package.ToModel());
if (response.Success)
_cachingService.UpdatePackageMetadata(response.Package);
return response;
}
public async Task<PackageThumbnailResponse> 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<PackageUploadedUnityVersionDataResponse> 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();
}
}
}

View File

@@ -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

View File

@@ -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<float> Progress = new Progress<float>();
public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
public UploadInProgress(IPackageUploader uploader, IProgress<float> progress)
{
Uploader = uploader;
Progress = progress;
CancellationTokenSource = new CancellationTokenSource();
}
}
private IAssetStoreApi _api;
private List<UploadInProgress> _uploadsInProgress;
public bool IsUploading => _uploadsInProgress.Count > 0;
public PackageUploadingService(IAssetStoreApi api)
{
_api = api;
_uploadsInProgress = new List<UploadInProgress>();
}
public async Task<PackageUploadResponse> UploadPackage(IPackageUploader uploader, IProgress<float> 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<float> 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();
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a834946d92154754493879c5fcc7dbc9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<Package> data)
{
data = new List<Package>();
if (!CacheUtil.GetFileFromTempCache(Constants.Cache.PackageDataFileName, out var filePath))
return false;
try
{
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = Package.CachedPackageResolver.Instance
};
data = JsonConvert.DeserializeObject<List<Package>>(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings);
return true;
}
catch
{
return false;
}
}
public void CachePackageMetadata(List<Package> 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<WorkflowStateData>(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);
}
}
}

View File

@@ -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

View File

@@ -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<Package> data);
void UpdatePackageMetadata(Package data);
void CachePackageMetadata(List<Package> 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);
}
}

View File

@@ -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

View File

@@ -0,0 +1,4 @@
namespace AssetStoreTools.Uploader.Services
{
internal interface IUploaderService { }
}

View File

@@ -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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 02e4a5ee9e2fb7941b876b207078e01d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<IPackage> packages);
IPackage CreatePackage(PackageModel packageModel);
IPackageContent CreatePackageContent(IPackage package);
List<IWorkflow> CreateWorkflows(IPackage package, WorkflowStateData stateData);
AssetsWorkflow CreateAssetsWorkflow(IPackage package, AssetsWorkflowState stateData);
UnityPackageWorkflow CreateUnityPackageWorkflow(IPackage package, UnityPackageWorkflowState stateData);
HybridPackageWorkflow CreateHybridPackageWorkflow(IPackage package, HybridPackageWorkflowState stateData);
}
}

View File

@@ -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

View File

@@ -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<IPackage> 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<IWorkflow> CreateWorkflows(IPackage package, WorkflowStateData stateData)
{
var workflows = new List<IWorkflow>
{
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;
}
}
}

View File

@@ -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

View File

@@ -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<IUploaderService>
{
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<IAnalyticsService, AnalyticsService>();
Register<ICachingService, CachingService>();
Register<IAuthenticationService>(() => new AuthenticationService(api, GetService<ICachingService>(), GetService<IAnalyticsService>()));
Register<IPackageDownloadingService>(() => new PackageDownloadingService(api, GetService<ICachingService>()));
Register<IPackageUploadingService>(() => new PackageUploadingService(api));
Register<IPackageFactoryService, PackageFactoryService>();
}
}
}

View File

@@ -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

View File

@@ -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<LoginWindow> { }
#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<JsonValue> onSuccess, Action<ASError> onFail)
{
this.SetEnabled(true);
_cloudLoginLabel = _cloudLoginButton.Q<Label>(className: "login-description");
_cloudLoginLabel.text = "Cloud login unavailable.";
_cloudLoginButton.SetEnabled(false);
_cloudLoginButton.clicked += async () =>
{
EnableErrorBox(false);
this.SetEnabled(false);
var result = await AssetStoreAPI.LoginWithTokenAsync(CloudProjectSettings.accessToken);
if (result.Success)
onSuccess(result.Response);
else
onFail(result.Error);
};
// Normal login
_credentialsLoginButton.clicked += async () =>
{
EnableErrorBox(false);
var validatedFields = ValidateLoginFields(_emailField.text, _passwordField.value);
this.SetEnabled(!validatedFields);
if (validatedFields)
{
var result = await AssetStoreAPI.LoginWithCredentialsAsync(_emailField.text, _passwordField.text);
if (result.Success)
onSuccess(result.Response);
else
onFail(result.Error);
}
};
}
public void EnableErrorBox(bool enable, string message = null)
{
var displayStyle = enable ? DisplayStyle.Flex : DisplayStyle.None;
_errorBox.style.display = displayStyle;
if (!String.IsNullOrEmpty(message))
_errorLabel.text = message;
}
public void ClearLoginBoxes()
{
_emailField.value = String.Empty;
_passwordField.value = String.Empty;
}
private bool ValidateLoginFields(string email, string password)
{
if (string.IsNullOrEmpty(email))
{
EnableErrorBox(true, "Email field cannot be empty.");
return false;
}
if (string.IsNullOrEmpty(password))
{
EnableErrorBox(true, "Password field cannot be empty.");
return false;
}
return true;
}
private void ConstructLoginWindow()
{
// Asset Store logo
Image assetStoreLogo = new Image { name = "AssetStoreLogo" };
assetStoreLogo.AddToClassList("asset-store-logo");
Add(assetStoreLogo);
// Cloud login
VisualElement cloudLogin = new VisualElement { name = "CloudLogin" };
_cloudLoginButton = new Button { name = "LoginButtonCloud" };
_cloudLoginButton.AddToClassList("login-button-cloud");
Label loginDescription = new Label { text = "Cloud login unavailable" };
loginDescription.AddToClassList("login-description");
Label orLabel = new Label { text = "or" };
orLabel.AddToClassList("or-label");
_cloudLoginButton.Add(loginDescription);
cloudLogin.Add(_cloudLoginButton);
cloudLogin.Add(orLabel);
Add(cloudLogin);
_errorBox = new Box() { name = "LoginErrorBox" };
_errorBox.AddToClassList("login-error-box");
var errorImage = new Image();
_errorBox.Add(errorImage);
_errorLabel = new Label();
_errorBox.Add(_errorLabel);
Add(_errorBox);
EnableErrorBox(false);
// Manual login
VisualElement manualLoginBox = new VisualElement { name = "ManualLoginBox" };
manualLoginBox.AddToClassList("manual-login-box");
// Email input box
VisualElement inputBoxEmail = new VisualElement();
inputBoxEmail.AddToClassList("input-box-login");
Label emailTitle = new Label { text = "Email" };
_emailField = new TextField();
inputBoxEmail.Add(emailTitle);
inputBoxEmail.Add(_emailField);
manualLoginBox.Add(inputBoxEmail);
// Password input box
VisualElement inputBoxPassword = new VisualElement();
inputBoxPassword.AddToClassList("input-box-login");
Label passwordTitle = new Label { text = "Password" };
_passwordField = new TextField { isPasswordField = true };
inputBoxPassword.Add(passwordTitle);
inputBoxPassword.Add(_passwordField);
manualLoginBox.Add(inputBoxPassword);
// Login button
_credentialsLoginButton = new Button { name = "LoginButtonCredentials" };
_credentialsLoginButton.AddToClassList("login-button-cred");
Label loginDescriptionCredentials = new Label { text = "Login" };
loginDescriptionCredentials.AddToClassList("login-description");
_credentialsLoginButton.Add(loginDescriptionCredentials);
manualLoginBox.Add(_credentialsLoginButton);
Add(manualLoginBox);
// Helper buttons
VisualElement helperBox = new VisualElement { name = "HelperBox" };
helperBox.AddToClassList("helper-button-box");
Button createAccountButton = new Button { name = "CreateAccountButton", text = "Create Publisher ID" };
Button forgotPasswordButton = new Button { name = "ForgotPasswordButton", text = "Reset Password" };
createAccountButton.AddToClassList("hyperlink-button");
forgotPasswordButton.AddToClassList("hyperlink-button");
createAccountButton.clicked += () => Application.OpenURL(REGISTER_URL);
forgotPasswordButton.clicked += () => Application.OpenURL(FORGOT_PASSWORD_URL);
helperBox.Add(createAccountButton);
helperBox.Add(forgotPasswordButton);
Add(helperBox);
}
private void UpdateCloudLoginButton()
{
if (_cloudLoginLabel == null)
return;
if (_lastRefreshTime + _cloudLoginRefreshTime > EditorApplication.timeSinceStartup)
return;
_lastRefreshTime = EditorApplication.timeSinceStartup;
// Cloud login
if (AssetStoreAPI.IsCloudUserAvailable)
{
_cloudLoginLabel.text = $"Login with {CloudProjectSettings.userName}";
_cloudLoginButton.SetEnabled(true);
}
else
{
_cloudLoginLabel.text = "Cloud login unavailable";
_cloudLoginButton.SetEnabled(false);
}
}
}
}

View File

@@ -1,413 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AssetStoreTools.Uploader.Data;
using AssetStoreTools.Uploader.Utility;
using AssetStoreTools.Utility;
using AssetStoreTools.Validator.Data;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace AssetStoreTools.Uploader.UIElements
{
#if UNITY_6000_0_OR_NEWER
[UxmlElement]
#endif
internal partial class AllPackageView : VisualElement
{
#if !UNITY_6000_0_OR_NEWER
public new class UxmlFactory : UxmlFactory<AllPackageView> { }
#endif
private enum PackageSorting
{
Name,
Category,
Date
}
// Package Data
private readonly string[] _priorityGroups = { "Draft", "Published" };
private readonly List<PackageGroup> _packageGroups;
private List<PackageView> _allPackages;
// Visual Elements
private readonly ScrollView _packageScrollView;
private Scroller _packageViewScroller;
// Sorting data
private PackageSorting _activeSorting;
// Spinner
private VisualElement _spinnerBox;
private Image _loadingSpinner;
private int _spinIndex;
private double _spinTimer;
private double _spinThreshold = 0.1;
public Action<bool> RefreshingPackages;
public AllPackageView()
{
_packageScrollView = new ScrollView();
_allPackages = new List<PackageView>();
_packageGroups = new List<PackageGroup>();
_activeSorting = PackageSorting.Name; // Default sorting type returned by the metadata JSON
styleSheets.Add(StyleSelector.UploaderWindow.AllPackagesStyle);
styleSheets.Add(StyleSelector.UploaderWindow.AllPackagesTheme);
ConstructAllPackageView();
EditorApplication.playModeStateChanged -= PlayModeStateChanged;
EditorApplication.playModeStateChanged += PlayModeStateChanged;
ValidationState.Instance.OnJsonSave -= RepaintPackageIcons;
ValidationState.Instance.OnJsonSave += RepaintPackageIcons;
}
#region Element Setup
private void ConstructAllPackageView()
{
SetupFilteringTools();
SetupSpinner();
Add(_packageScrollView);
}
private void SetupFilteringTools()
{
// Top Toolbar
var topToolsRow = new VisualElement { name = "TopToolsRow" };
topToolsRow.AddToClassList("top-tools-row");
// Search
var searchField = new ToolbarSearchField { name = "SearchField" };
searchField.AddToClassList("package-search-field");
// Sorting menu button
var sortMenu = new ToolbarMenu() { text = "Sort by name, A→Z" };
sortMenu.menu.AppendAction("Sort by name, A→Z", (_) => { sortMenu.text = "Sort by name, A→Z"; Sort(PackageSorting.Name); });
sortMenu.menu.AppendAction("Sort by last updated", (_) => { sortMenu.text = "Sort by last updated"; Sort(PackageSorting.Date); });
sortMenu.menu.AppendAction("Sort by category, A→Z", (_) => { sortMenu.text = "Sort by category, A→Z"; Sort(PackageSorting.Category); });
sortMenu.AddToClassList("sort-menu");
// Finalize the bar
topToolsRow.Add(searchField);
topToolsRow.Add(sortMenu);
Add(topToolsRow);
// Add Callbacks and click events
searchField.RegisterCallback<ChangeEvent<string>>(evt =>
{
var searchString = evt.newValue.ToLower();
SearchFilter(searchString);
});
}
private void SearchFilter(string filter)
{
foreach (var g in _packageGroups)
g.SearchFilter(filter);
}
private void SetupReadOnlyInfoBox(string infoText)
{
var groupHeader = new Box { name = "GroupReadOnlyInfoBox" };
groupHeader.AddToClassList("group-info-box");
var infoImage = new Image();
groupHeader.Add(infoImage);
var infoLabel = new Label { text = infoText };
groupHeader.Add(infoLabel);
_packageScrollView.Add(groupHeader);
}
#endregion
#region Package Display
public async void ShowPackagesList(bool useCached, Action<ASError> onFail)
{
// Clear existing packages in the UI
ClearPackages();
// Enable spinner and disable refreshing
EnableSpinner();
RefreshingPackages?.Invoke(true);
// Read package metadata from the Publisher portal
PackageFetcher packageFetcher = new PackageFetcher();
var result = await packageFetcher.Fetch(useCached);
if (!result.Success)
{
if (result.SilentFail)
return;
ASDebug.LogError(result.Error.Message);
onFail?.Invoke(result.Error);
}
var packages = result.Packages;
var json = result.Json;
// Clear before appending as well
ClearPackages();
if (packages == null)
{
RefreshingPackages?.Invoke(false);
DisplayNoPackages();
return;
}
DisplayAllPackages(packages);
// Only performed after adding all packages to prevent slowdowns. Sorting also repaints the view
Sort(_activeSorting);
RefreshingPackages?.Invoke(false);
DisableSpinner();
AssetStoreAPI.GetPackageThumbnails(json, true, (id, texture) =>
{
var package = GetPackage(id);
var packageImage = package.Q<Image>();
packageImage.style.backgroundImage = texture;
if (texture == null)
packageImage.AddToClassList("package-image-not-found");
},
(id, error) =>
{
ASDebug.LogWarning($"Package {id} could not download thumbnail successfully\n{error.Exception}");
});
}
public void ClearPackages()
{
_allPackages.Clear();
_packageScrollView.Clear();
_packageGroups.Clear();
}
private void DisplayNoPackages()
{
SetupReadOnlyInfoBox("You don't have packages yet. Please visit Publishing Portal if you " +
"would like to create one.");
DisableSpinner();
}
private void DisplayAllPackages(ICollection<PackageData> packages)
{
// Each package has an identifier and a bunch of data (current version id, name, etc.)
foreach (var package in packages)
{
AddPackage(package);
}
}
private void AddPackage(PackageData packageData)
{
var newEntry = PackageViewStorer.GetPackage(packageData);
_allPackages.Add(newEntry);
}
private VisualElement GetPackage(string id)
{
return _allPackages.FirstOrDefault(package => package.PackageId == id);
}
private void Repaint()
{
_packageScrollView.Clear();
_packageGroups.Clear();
var groupedDict = new SortedDictionary<string, List<PackageView>>();
// Group packages by status into a dictionary
foreach (var p in _allPackages)
{
var status = char.ToUpper(p.Status.First()) + p.Status.Substring(1);
if (!groupedDict.ContainsKey(status))
groupedDict.Add(status, new List<PackageView>());
groupedDict[status].Add(p);
}
// Add prioritized status groups first
foreach (var group in _priorityGroups)
{
if (!groupedDict.ContainsKey(group))
continue;
AddGroup(group, groupedDict[group], true);
groupedDict.Remove(group);
// After adding the 'Draft' group, an infobox indicating that other groups are non-interactable is added
if (group == "Draft" && groupedDict.Count > 0)
SetupReadOnlyInfoBox("Only packages with a 'Draft' status can be selected for uploading Assets");
}
// Add any leftover status groups
foreach (var c in groupedDict.Keys)
{
AddGroup(c, groupedDict[c], false);
}
// Shared group adding method for priority and non-priority groups
void AddGroup(string groupName, List<PackageView> packages, bool createExpanded)
{
var group = new PackageGroup(groupName, createExpanded)
{
OnSliderChange = AdjustVerticalSliderPosition
};
foreach (var p in packages)
group.AddPackage(p);
_packageGroups.Add(group);
_packageScrollView.Add(group);
}
}
private void RepaintPackageIcons()
{
foreach (var package in _allPackages)
{
if (!AssetStoreCache.GetCachedTexture(package.PackageId, out Texture2D texture))
continue;
var packageImage = package.Q<Image>();
packageImage.style.backgroundImage = texture;
}
}
private void AdjustVerticalSliderPosition(float delta)
{
if (_packageViewScroller == null)
_packageViewScroller = this.Q<Scroller>(className: "unity-scroll-view__vertical-scroller");
_packageViewScroller.value += delta;
}
#endregion
#region Package View Sorting
private void Sort(PackageSorting sortBy)
{
if (sortBy == _activeSorting && _packageScrollView.childCount > 0)
return;
switch (sortBy)
{
case PackageSorting.Name:
SortByName(false);
break;
case PackageSorting.Date:
SortByDate(true);
break;
case PackageSorting.Category:
SortByCategory(false);
break;
}
_activeSorting = sortBy;
}
private void SortByName(bool descending)
{
if (!descending)
_allPackages = _allPackages.OrderBy(p => p.PackageName).ToList();
else
_allPackages = _allPackages.OrderByDescending(p => p.PackageName).ToList();
Repaint();
}
private void SortByCategory(bool descending)
{
if (!descending)
_allPackages = _allPackages.OrderBy(p => p.Category).ThenBy(p => p.PackageName).ToList();
else
_allPackages = _allPackages.OrderByDescending(p => p.Category).ThenBy(p => p.PackageName).ToList();
Repaint();
}
private void SortByDate(bool descending)
{
if (!descending)
_allPackages = _allPackages.OrderBy(p => p.LastUpdatedDate).ThenBy(p => p.PackageName).ToList();
else
_allPackages = _allPackages.OrderByDescending(p => p.LastUpdatedDate).ThenBy(p => p.PackageName).ToList();
Repaint();
}
private void PlayModeStateChanged(PlayModeStateChange playModeState)
{
if (playModeState == PlayModeStateChange.EnteredEditMode)
{
RepaintPackageIcons();
}
}
#endregion
#region Spinner
private void SetupSpinner()
{
_spinnerBox = new VisualElement { name = "SpinnerBox" };
_spinnerBox.AddToClassList("spinner-box");
_loadingSpinner = new Image { name = "SpinnerImage" };
_loadingSpinner.AddToClassList("spinner-image");
_spinnerBox.Add(_loadingSpinner);
Add(_spinnerBox);
}
private void UpdateSpinner()
{
if (_loadingSpinner == null)
return;
if (_spinTimer + _spinThreshold > EditorApplication.timeSinceStartup)
return;
_spinTimer = EditorApplication.timeSinceStartup;
_loadingSpinner.image = EditorGUIUtility.IconContent($"WaitSpin{_spinIndex:00}").image;
_spinIndex += 1;
if (_spinIndex > 11)
_spinIndex = 0;
}
private void EnableSpinner()
{
EditorApplication.update += UpdateSpinner;
_packageScrollView.style.display = DisplayStyle.None;
_spinnerBox.style.display = DisplayStyle.Flex;
}
private void DisableSpinner()
{
EditorApplication.update -= UpdateSpinner;
_packageScrollView.style.display = DisplayStyle.Flex;
_spinnerBox.style.display = DisplayStyle.None;
}
#endregion
}
}

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 607cf9e3fb4a49839f2e6a82e0d8d535
timeCreated: 1651220955
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/AllPackageView.cs
uploadId: 712972

View File

@@ -1,180 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine.UIElements;
namespace AssetStoreTools.Uploader.UIElements
{
internal class PackageGroup : VisualElement
{
// Category Data
private string GroupName { get; }
private readonly List<PackageView> _packages;
// Visual Elements
private Button _groupExpanderBox;
private VisualElement _groupContent;
private Label _expanderLabel;
private Label _groupLabel;
// Other
private PackageView _expandedPackageView;
private bool _expanded;
private bool? _expandingOverriden;
// Actions
public Action<float> OnSliderChange;
public PackageGroup(string groupName, bool createExpanded)
{
GroupName = groupName;
AddToClassList("package-group");
_packages = new List<PackageView>();
_expanded = createExpanded;
SetupSingleGroupElement();
HandleExpanding();
}
public void AddPackage(PackageView packageView)
{
_packages.Add(packageView);
_groupContent.Add(packageView);
UpdateGroupLabel();
packageView.OnPackageSelection = HandlePackageSelection;
packageView.ShowFunctions(false);
}
public void SearchFilter(string filter)
{
var foundPackageCount = 0;
foreach(var p in _packages)
{
if (p.SearchableText.Contains(filter))
{
foundPackageCount++;
p.style.display = DisplayStyle.Flex;
_groupContent.style.display = DisplayStyle.Flex;
}
else
p.style.display = DisplayStyle.None;
}
if (string.IsNullOrEmpty(filter))
{
_expandingOverriden = null;
UpdateGroupLabel();
SetEnabled(true);
HandleExpanding();
}
else
{
OverwriteGroupLabel($"{GroupName} ({foundPackageCount} found)");
SetEnabled(foundPackageCount > 0);
HandleExpanding(foundPackageCount > 0);
}
}
private void SetupSingleGroupElement()
{
_groupExpanderBox = new Button();
_groupExpanderBox.AddToClassList("group-expander-box");
_expanderLabel = new Label { name = "ExpanderLabel", text = "►" };
_expanderLabel.AddToClassList("expander");
_groupLabel = new Label {text = $"{GroupName} ({_packages.Count})"};
_groupLabel.AddToClassList("group-label");
_groupExpanderBox.Add(_expanderLabel);
_groupExpanderBox.Add(_groupLabel);
_groupContent = new VisualElement {name = "GroupContentBox"};
_groupContent.AddToClassList("group-content-box");
_groupExpanderBox.clicked += () =>
{
if (_expandingOverriden == null)
_expanded = !_expanded;
else
_expandingOverriden = !_expandingOverriden;
HandleExpanding();
};
var groupSeparator = new VisualElement {name = "GroupSeparator"};
groupSeparator.AddToClassList("group-separator");
if (GroupName.ToLower() != "draft")
{
_groupLabel.SetEnabled(false);
_groupContent.AddToClassList("unity-disabled");
groupSeparator.style.display = DisplayStyle.Flex;
}
Add(_groupExpanderBox);
Add(_groupContent);
Add(groupSeparator);
}
private void HandleExpanding(bool? overrideExpanding=null)
{
var expanded = _expanded;
if (overrideExpanding != null)
{
expanded = (bool) overrideExpanding;
_expandingOverriden = expanded;
}
else
{
if (_expandingOverriden != null)
expanded = (bool) _expandingOverriden;
}
_expanderLabel.text = !expanded ? "►" : "▼";
var displayStyle = expanded ? DisplayStyle.Flex : DisplayStyle.None;
_groupContent.style.display = displayStyle;
}
private void HandlePackageSelection(PackageView packageView)
{
if (_expandedPackageView == packageView)
{
_expandedPackageView = null;
return;
}
if (_expandedPackageView == null)
{
_expandedPackageView = packageView;
return;
}
// Always where it was
if (packageView.worldBound.y > _expandedPackageView.worldBound.y)
{
var sliderChangeDelta = -(_expandedPackageView.worldBound.height - packageView.worldBound.height);
OnSliderChange?.Invoke(sliderChangeDelta);
}
_expandedPackageView?.ShowFunctions(false);
_expandedPackageView = packageView;
}
private void UpdateGroupLabel()
{
_groupLabel.text = $"{GroupName} ({_packages.Count})";
}
private void OverwriteGroupLabel(string text)
{
_groupLabel.text = text;
}
}
}

View File

@@ -1,18 +0,0 @@
fileFormatVersion: 2
guid: dd683831688cd414f8cc9cd352689b4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
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/PackageGroup.cs
uploadId: 712972

View File

@@ -1,720 +0,0 @@
using AssetStoreTools.Utility.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Text.RegularExpressions;
using AssetStoreTools.Utility;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using AssetStoreTools.Exporter;
using AssetStoreTools.Uploader.Data;
using AssetStoreTools.Uploader.Utility;
namespace AssetStoreTools.Uploader.UIElements
{
internal class PackageView : VisualElement
{
public string PackageId => _packageData.Id;
public string VersionId => _packageData.VersionId;
public string PackageName => _packageData.Name;
public string Status => _packageData.Status;
public string Category => _packageData.Category;
public string LastUpdatedDate => FormatDate(_packageData.LastDate);
public string LastUpdatedSize => FormatSize(_packageData.LastSize);
public bool IsCompleteProject => _packageData.IsCompleteProject;
public string LastUploadedPath => _packageData.LastUploadedPath;
public string LastUploadedGuid => _packageData.LastUploadedGuid;
public string SearchableText { get; private set; }
private PackageData _packageData;
// Unexpanded state dynamic elements
private Button _foldoutBox;
private Label _expanderLabel;
private Label _assetLabel;
private Label _lastDateSizeLabel;
private Button _openInBrowserButton;
// Expanded state dynamic elements
private VisualElement _functionsBox;
private VisualElement _exportAndUploadContainer;
private VisualElement _uploadProgressContainer;
private Button _exportButton;
private Button _uploadButton;
private Button _cancelUploadButton;
private ProgressBar _uploadProgressBarFlow;
private ProgressBar _uploadProgressBarHeader;
private VisualElement _uploadProgressFlowBg;
private VisualElement _uploadProgressHeaderBg;
private bool _expanded;
public Action<PackageView> OnPackageSelection;
private VisualElement _workflowSelectionBox;
private UploadWorkflowView _activeWorkflowElement;
private Dictionary<string, UploadWorkflowView> _uploadWorkflows;
public PackageView(PackageData packageData)
{
UpdateDataValues(packageData);
SetupPackageElement();
}
public void UpdateDataValues(PackageData packageData)
{
_packageData = packageData;
SearchableText = $"{PackageName} {Category}".ToLower();
if (_foldoutBox == null)
return;
_assetLabel.text = PackageName;
_lastDateSizeLabel.text = $"{Category} | {LastUpdatedSize} | {LastUpdatedDate}";
if (_uploadWorkflows != null && _uploadWorkflows.ContainsKey(FolderUploadWorkflowView.WorkflowName))
((FolderUploadWorkflowView) _uploadWorkflows[FolderUploadWorkflowView.WorkflowName]).SetCompleteProject(packageData.IsCompleteProject);
if (Status == "draft")
return;
ResetPostUpload();
SetupExpander();
}
public void ShowFunctions(bool show)
{
if (_functionsBox == null)
{
if (show)
SetupFunctionsElement();
else
return;
}
if (show == _expanded)
return;
_expanded = show;
_expanderLabel.text = !_expanded ? "►" : "▼";
if (_expanded)
_foldoutBox.AddToClassList("foldout-box-expanded");
else
_foldoutBox.RemoveFromClassList("foldout-box-expanded");
if (_functionsBox != null)
_functionsBox.style.display = show ? DisplayStyle.Flex : DisplayStyle.None;
}
private void SetupPackageElement()
{
AddToClassList("full-package-box");
_foldoutBox = new Button {name = "Package"};
_foldoutBox.AddToClassList("foldout-box");
// Expander, Icon and Asset Label
VisualElement foldoutBoxInfo = new VisualElement { name = "foldoutBoxInfo" };
foldoutBoxInfo.AddToClassList("foldout-box-info");
VisualElement labelExpanderRow = new VisualElement { name = "labelExpanderRow" };
labelExpanderRow.AddToClassList("expander-label-row");
_expanderLabel = new Label { name = "ExpanderLabel", text = "►" };
_expanderLabel.AddToClassList("expander");
Image assetImage = new Image { name = "AssetImage" };
assetImage.AddToClassList("package-image");
VisualElement assetLabelInfoBox = new VisualElement { name = "assetLabelInfoBox" };
assetLabelInfoBox.AddToClassList("asset-label-info-box");
_assetLabel = new Label { name = "AssetLabel", text = PackageName };
_assetLabel.AddToClassList("asset-label");
_lastDateSizeLabel = new Label {name = "AssetInfoLabel", text = $"{Category} | {LastUpdatedSize} | {LastUpdatedDate}"};
_lastDateSizeLabel.AddToClassList("asset-info");
assetLabelInfoBox.Add(_assetLabel);
assetLabelInfoBox.Add(_lastDateSizeLabel);
labelExpanderRow.Add(_expanderLabel);
labelExpanderRow.Add(assetImage);
labelExpanderRow.Add(assetLabelInfoBox);
_openInBrowserButton = new Button
{
name = "OpenInBrowserButton",
tooltip = "View your package in the Publishing Portal."
};
_openInBrowserButton.AddToClassList("open-in-browser-button");
// Header Progress bar
_uploadProgressBarHeader = new ProgressBar { name = "HeaderProgressBar" };
_uploadProgressBarHeader.AddToClassList("header-progress-bar");
_uploadProgressBarHeader.style.display = DisplayStyle.None;
_uploadProgressHeaderBg = _uploadProgressBarHeader.Q<VisualElement>(className:"unity-progress-bar__progress");
// Connect it all
foldoutBoxInfo.Add(labelExpanderRow);
foldoutBoxInfo.Add(_openInBrowserButton);
_foldoutBox.Add(foldoutBoxInfo);
_foldoutBox.Add(_uploadProgressBarHeader);
Add(_foldoutBox);
SetupExpander();
}
private void SetupExpander()
{
if (_foldoutBox != null)
{
_foldoutBox.clickable = null;
// If not draft - hide expander, open a listing page on click
if (Status != "draft")
{
_expanderLabel.style.display = DisplayStyle.None;
_foldoutBox.clicked += () =>
{
Application.OpenURL($"https://publisher.unity.com/packages/{VersionId}/edit/upload");
};
}
else
{
// Else open functions box
_foldoutBox.clicked += () =>
{
OnPackageSelection?.Invoke(this);
ShowFunctions(!_expanded);
};
}
}
if (_openInBrowserButton != null)
{
_openInBrowserButton.clickable = null;
_openInBrowserButton.clicked += () =>
{
Application.OpenURL($"https://publisher.unity.com/packages/{VersionId}/edit/upload");
};
}
}
private void SetupFunctionsElement()
{
_functionsBox = new VisualElement { name = "FunctionalityBox" };
_functionsBox.AddToClassList("functionality-box");
_functionsBox.style.display = DisplayStyle.None;
// Validation and uploading boxes
var uploadingWorkflow = ConstructUploadingWorkflow();
_functionsBox.Add(uploadingWorkflow);
Add(_functionsBox);
}
private VisualElement ConstructUploadingWorkflow()
{
// Upload Box
VisualElement uploadBox = new VisualElement { name = "UploadBox" };
uploadBox.AddToClassList("upload-box");
var folderUploadWorkflow = FolderUploadWorkflowView.Create(Category, IsCompleteProject, SerializeWorkflowSelections);
var unitypackageUploadWorkflow = UnityPackageUploadWorkflowView.Create(Category, SerializeWorkflowSelections);
var hybridPackageUploadWorkflow = HybridPackageUploadWorkflowView.Create(Category, SerializeWorkflowSelections);
// Workflow selection
_workflowSelectionBox = new VisualElement();
_workflowSelectionBox.AddToClassList("selection-box-row");
VisualElement labelHelpRow = new VisualElement();
labelHelpRow.AddToClassList("label-help-row");
Label workflowLabel = new Label { text = "Upload type" };
Image workflowLabelTooltip = new Image
{
tooltip = "Select what content you are uploading to the Asset Store"
+ "\n\n• From Assets Folder - content located within the project's 'Assets' folder or one of its subfolders"
+ "\n\n• Pre-exported .unitypackage - content that has already been compressed into a .unitypackage file"
#if UNITY_ASTOOLS_EXPERIMENTAL
+ "\n\n• Local UPM Package - content that is located within the project's 'Packages' folder. Only embedded and local packages are supported"
#endif
};
labelHelpRow.Add(workflowLabel);
labelHelpRow.Add(workflowLabelTooltip);
var flowDrop = new ToolbarMenu();
flowDrop.menu.AppendAction(FolderUploadWorkflowView.WorkflowDisplayName, _ => { SetActiveWorkflowElement(folderUploadWorkflow, flowDrop); });
flowDrop.menu.AppendAction(UnityPackageUploadWorkflowView.WorkflowDisplayName, _ => { SetActiveWorkflowElement(unitypackageUploadWorkflow, flowDrop); });
#if UNITY_ASTOOLS_EXPERIMENTAL
flowDrop.menu.AppendAction(HybridPackageUploadWorkflowView.WorkflowDisplayName, _ => { SetActiveWorkflowElement(hybridPackageUploadWorkflow, flowDrop); });
#endif // UNITY_ASTOOLS_EXPERIMENTAL
flowDrop.AddToClassList("workflow-dropdown");
_workflowSelectionBox.Add(labelHelpRow);
_workflowSelectionBox.Add(flowDrop);
uploadBox.Add(_workflowSelectionBox);
_uploadWorkflows = new Dictionary<string, UploadWorkflowView>
{
{FolderUploadWorkflowView.WorkflowName, folderUploadWorkflow},
{UnityPackageUploadWorkflowView.WorkflowName, unitypackageUploadWorkflow},
{HybridPackageUploadWorkflowView.WorkflowName, hybridPackageUploadWorkflow}
};
foreach (var kvp in _uploadWorkflows)
uploadBox.Add(kvp.Value);
var progressUploadBox = SetupProgressUploadBox();
uploadBox.Add(progressUploadBox);
DeserializeWorkflowSelections(flowDrop);
return uploadBox;
}
private void SerializeWorkflowSelections()
{
ASDebug.Log("Serializing workflow selections");
var json = JsonValue.NewDict();
// Active workflow
var activeWorkflow = JsonValue.NewString(_activeWorkflowElement.Name);
json["ActiveWorkflow"] = activeWorkflow;
// Workflow Selections
foreach(var kvp in _uploadWorkflows)
json[kvp.Key] = kvp.Value.SerializeWorkflow();
AssetStoreCache.CacheUploadSelections(PackageId, json);
}
private void DeserializeWorkflowSelections(ToolbarMenu activeFlowMenu)
{
AssetStoreCache.GetCachedUploadSelections(PackageId, out JsonValue cachedSelections);
// Individual workflow selections
foreach (var kvp in _uploadWorkflows)
{
if (cachedSelections.ContainsKey(kvp.Key))
kvp.Value.LoadSerializedWorkflow(cachedSelections[kvp.Key], LastUploadedPath, LastUploadedGuid);
else
kvp.Value.LoadSerializedWorkflowFallback(LastUploadedPath, LastUploadedGuid);
}
// Active workflow selection
if (!cachedSelections.ContainsKey("ActiveWorkflow"))
{
// Set default to folder workflow
SetActiveWorkflowElement(_uploadWorkflows[FolderUploadWorkflowView.WorkflowName], activeFlowMenu);
return;
}
var serializedWorkflow = cachedSelections["ActiveWorkflow"].AsString();
SetActiveWorkflowElement(_uploadWorkflows[serializedWorkflow], activeFlowMenu);
}
private void SetActiveWorkflowElement(UploadWorkflowView newActiveWorkflowElement, ToolbarMenu activeFlowMenu)
{
if (_activeWorkflowElement != null)
_activeWorkflowElement.style.display = DisplayStyle.None;
_activeWorkflowElement = newActiveWorkflowElement;
_activeWorkflowElement.style.display = DisplayStyle.Flex;
activeFlowMenu.text = newActiveWorkflowElement.DisplayName;
UpdateActionButtons();
SerializeWorkflowSelections();
}
private void UpdateActionButtons()
{
switch (_activeWorkflowElement)
{
case UnityPackageUploadWorkflowView _:
_exportButton.style.display = DisplayStyle.None;
_uploadButton.style.marginLeft = 0f;
_uploadButton.text = "Upload";
break;
default:
_exportButton.style.display = DisplayStyle.Flex;
_uploadButton.style.marginLeft = 5f;
_uploadButton.text = "Export and Upload";
break;
}
}
private VisualElement SetupProgressUploadBox()
{
var progressUploadBox = new VisualElement();
progressUploadBox.AddToClassList("progress-upload-box");
_exportAndUploadContainer = new VisualElement();
_exportAndUploadContainer.AddToClassList("export-and-upload-container");
_exportButton = new Button(ExportWithoutUploading) { name = "ExportButton", text = "Export" };
_exportButton.AddToClassList("export-button");
_uploadButton = new Button(PreparePackageUpload) { name = "UploadButton", text = "Export and Upload" };
_uploadButton.AddToClassList("upload-button");
_exportAndUploadContainer.Add(_exportButton);
_exportAndUploadContainer.Add(_uploadButton);
_uploadProgressContainer = new VisualElement();
_uploadProgressContainer.AddToClassList("upload-progress-container");
_uploadProgressContainer.style.display = DisplayStyle.None;
_uploadProgressBarFlow = new ProgressBar { name = "UploadProgressBar" };
_uploadProgressBarFlow.AddToClassList("upload-progress-bar");
_uploadProgressFlowBg = _uploadProgressBarFlow.Q<VisualElement>(className: "unity-progress-bar__progress");
_cancelUploadButton = new Button() { name = "CancelButton", text = "Cancel" };
_cancelUploadButton.AddToClassList("cancel-button");
_uploadProgressContainer.Add(_uploadProgressBarFlow);
_uploadProgressContainer.Add(_cancelUploadButton);
progressUploadBox.Add(_exportAndUploadContainer);
progressUploadBox.Add(_uploadProgressContainer);
return progressUploadBox;
}
private string FormatSize(string size)
{
if (string.IsNullOrEmpty(size))
return "0.00 MB";
float.TryParse(size, out var sizeBytes);
return $"{sizeBytes / (1024f * 1024f):0.00} MB";
}
private string FormatDate(string date)
{
DateTime dt = DateTime.Parse(date);
return dt.Date.ToString("yyyy-MM-dd");
}
#region Package Uploading
private async void ExportWithoutUploading()
{
var paths = _activeWorkflowElement.GetAllExportPaths();
if(paths.Length == 0)
{
EditorUtility.DisplayDialog("Exporting failed", "No path was selected. Please " +
"select a path and try again.", "OK");
return;
}
var rootProjectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length);
var packageNameStripped = Regex.Replace(PackageName, "[^a-zA-Z0-9]", "");
var outputPath = EditorUtility.SaveFilePanel("Export Package", rootProjectPath,
$"{packageNameStripped}-{DateTime.Now:yyyy-dd-M--HH-mm-ss}", "unitypackage");
if (string.IsNullOrEmpty(outputPath))
return;
var exportResult = await ExportPackage(outputPath);
if (!exportResult.Success)
Debug.LogError($"Package exporting failed: {exportResult.Error}");
else
Debug.Log($"Package exported to '{Path.GetFullPath(exportResult.ExportedPath).Replace("\\", "/")}'");
}
private async Task<ExportResult> ExportPackage(string outputPath)
{
var exportResult = await _activeWorkflowElement.ExportPackage(outputPath, IsCompleteProject);
return exportResult;
}
private bool ValidatePackageSize(string packagePath)
{
long packageSize = new FileInfo(packagePath).Length;
long packageSizeLimit = AssetStoreUploader.MaxPackageSizeBytes;
float packageSizeInGB = packageSize / (1024f * 1024f * 1024f);
float maxPackageSizeInGB = packageSizeLimit / (1024f * 1024f * 1024f);
if (packageSizeInGB - maxPackageSizeInGB < 0.1f)
return true;
var message = $"The size of your package ({packageSizeInGB:0.#} GB) exceeds the maximum allowed package size of {maxPackageSizeInGB:0.#} GB.\n\n" +
$"Please reduce the size of your package.";
EditorUtility.DisplayDialog("Asset Store Tools", message, "OK");
return false;
}
private bool ValidateUnityVersionsForUpload()
{
if (!AssetStoreUploader.ShowPackageVersionDialog)
return true;
EditorUtility.DisplayProgressBar("Preparing...", "Checking version compatibility", 0.4f);
var versions = AssetStoreAPI.GetPackageUploadedVersions(PackageId, VersionId);
EditorUtility.ClearProgressBar();
if (versions.Any(x => string.Compare(x, AssetStoreUploader.MinRequiredPackageVersion, StringComparison.Ordinal) >= 0))
return true;
var result = EditorUtility.DisplayDialogComplex("Asset Store Tools", $"You may upload this package, but you will need to add a package using Unity version {AssetStoreUploader.MinRequiredPackageVersion} " +
"or higher to be able to submit a new asset", "Upload", "Cancel", "Upload and do not display this again");
switch (result)
{
case 1:
return false;
case 2:
AssetStoreUploader.ShowPackageVersionDialog = false;
break;
}
return true;
}
private async void PreparePackageUpload()
{
var paths = _activeWorkflowElement.GetAllExportPaths();
if (paths.Length == 0)
{
EditorUtility.DisplayDialog("Uploading failed", "No path was selected. Please " +
"select a path and try again.", "OK");
return;
}
var packageNameStripped = Regex.Replace(PackageName, "[^a-zA-Z0-9]", "");
var outputPath = $"Temp/{packageNameStripped}-{DateTime.Now:yyyy-dd-M--HH-mm-ss}.unitypackage";
var exportResult = await ExportPackage(outputPath);
if (!exportResult.Success)
{
Debug.LogError($"Package exporting failed: {exportResult.Error}");
return;
}
if (!ValidatePackageSize(exportResult.ExportedPath))
return;
if (!ValidateUnityVersionsForUpload())
return;
var localPackageGuid = _activeWorkflowElement.GetLocalPackageGuid();
var localPackagePath = _activeWorkflowElement.GetLocalPackagePath();
var localProjectPath = _activeWorkflowElement.GetLocalProjectPath();
BeginPackageUpload(exportResult.ExportedPath, localPackageGuid, localPackagePath, localProjectPath);
}
private async void BeginPackageUpload(string exportedPackagePath, string packageGuid, string packagePath, string projectPath)
{
// Configure the UI
// Disable Active Workflow
EnableWorkflowElements(false);
// Progress bar
_exportAndUploadContainer.style.display = DisplayStyle.None;
_uploadProgressContainer.style.display = DisplayStyle.Flex;
// Configure the upload cancel button
_cancelUploadButton.clickable = null;
_cancelUploadButton.clicked += () => AssetStoreAPI.AbortPackageUpload(VersionId);
_cancelUploadButton.text = "Cancel";
// Set up upload progress tracking for the unexpanded package progress bar
EditorApplication.update += OnPackageUploadProgressHeader;
// Set up upload progress tracking for the expanded package progress bar
EditorApplication.update += OnPackageUploadProgressContent;
// Set up base analytics data
var analyticsData = ConstructAnalyticsData(exportedPackagePath);
// Start tracking uploading time
var watch = System.Diagnostics.Stopwatch.StartNew(); // Debugging
// Start uploading the package
var result = await AssetStoreAPI.UploadPackageAsync(VersionId, PackageName, exportedPackagePath, packageGuid, packagePath, projectPath);
watch.Stop();
analyticsData.TimeTaken = watch.Elapsed.TotalSeconds;
switch (result.Status)
{
case PackageUploadResult.UploadStatus.Success:
analyticsData.UploadFinishedReason = "Success";
ASDebug.Log($"Finished uploading, time taken: {watch.Elapsed.TotalSeconds} seconds");
await OnPackageUploadSuccess();
break;
case PackageUploadResult.UploadStatus.Cancelled:
analyticsData.UploadFinishedReason = "Cancelled";
ASDebug.Log($"Uploading cancelled, time taken: {watch.Elapsed.TotalSeconds} seconds");
break;
case PackageUploadResult.UploadStatus.Fail:
analyticsData.UploadFinishedReason = result.Error.Exception.ToString();
OnPackageUploadFail(result.Error);
break;
case PackageUploadResult.UploadStatus.ResponseTimeout:
analyticsData.UploadFinishedReason = "ResponseTimeout";
Debug.LogWarning($"All bytes for the package '{PackageName}' have been uploaded, but a response " +
$"from the server was not received. This can happen because of Firewall restrictions. " +
$"Please make sure that a new version of your package has reached the Publishing Portal.");
await OnPackageUploadSuccess();
break;
}
ASAnalytics.SendUploadingEvent(analyticsData);
PostUploadCleanup(result.Status);
}
private ASAnalytics.AnalyticsData ConstructAnalyticsData(string exportedPackagePath)
{
bool validated;
string validationResults;
validated = _activeWorkflowElement.GetValidationSummary(out validationResults);
FileInfo packageFileInfo = new FileInfo(exportedPackagePath);
string workflow = _activeWorkflowElement.Name;
ASAnalytics.AnalyticsData data = new ASAnalytics.AnalyticsData
{
ToolVersion = AssetStoreAPI.ToolVersion,
EndpointUrl = AssetStoreAPI.AssetStoreProdUrl,
PackageId = PackageId,
Category = Category,
UsedValidator = validated,
ValidatorResults = validationResults,
PackageSize = packageFileInfo.Length,
Workflow = workflow
};
return data;
}
private void OnPackageUploadProgressHeader()
{
// Header progress bar is only shown when the package is not expanded and has progress
if (_uploadProgressBarHeader.value > 0.0f)
_uploadProgressBarHeader.style.display = !_expanded ? DisplayStyle.Flex : DisplayStyle.None;
if (!AssetStoreAPI.ActiveUploads.ContainsKey(VersionId))
return;
_uploadProgressBarHeader.value = AssetStoreAPI.ActiveUploads[VersionId].Progress;
}
private void OnPackageUploadProgressContent()
{
if (!AssetStoreAPI.ActiveUploads.ContainsKey(VersionId))
return;
var progressValue = AssetStoreAPI.ActiveUploads[VersionId].Progress;
_uploadProgressBarFlow.value = progressValue;
_uploadProgressBarFlow.title = $"{progressValue:0.#}%";
if(progressValue == 100f && _cancelUploadButton.enabledInHierarchy)
_cancelUploadButton.SetEnabled(false);
}
private async Task OnPackageUploadSuccess()
{
if (ASToolsPreferences.Instance.DisplayUploadDialog)
EditorUtility.DisplayDialog("Success!", $"Package for '{PackageName}' has been uploaded successfully!", "OK");
SetEnabled(false);
PackageFetcher fetcher = new PackageFetcher();
var result = await fetcher.FetchRefreshedPackage(PackageId);
if(!result.Success)
{
ASDebug.LogError(result.Error);
SetEnabled(true);
return;
}
UpdateDataValues(result.Package);
ASDebug.Log($"Updated name, status, date and size values for package version id {VersionId}");
SetEnabled(true);
}
private void OnPackageUploadFail(ASError error)
{
if (ASToolsPreferences.Instance.DisplayUploadDialog)
EditorUtility.DisplayDialog("Upload failed", "Package uploading failed. See Console for details", "OK");
Debug.LogError(error);
}
private void PostUploadCleanup(PackageUploadResult.UploadStatus uploadStatus)
{
if (_activeWorkflowElement == null)
return;
SetProgressBarColorByStatus(uploadStatus);
_uploadProgressBarFlow.title = $"Upload: {uploadStatus.ToString()}";
_cancelUploadButton.clickable = null;
_cancelUploadButton.clicked += ResetPostUpload;
_cancelUploadButton.text = "Done";
// Re-enable the Cancel/Done button since it gets disabled at 100% progress
_cancelUploadButton.SetEnabled(true);
}
private void ResetPostUpload()
{
if (_activeWorkflowElement == null)
return;
// Cleanup the progress bars
EditorApplication.update -= OnPackageUploadProgressContent;
EditorApplication.update -= OnPackageUploadProgressHeader;
EnableWorkflowElements(true);
ResetProgressBar();
_exportAndUploadContainer.style.display = DisplayStyle.Flex;
_uploadProgressContainer.style.display = DisplayStyle.None;
}
private void ResetProgressBar()
{
SetProgressBarColorByStatus(PackageUploadResult.UploadStatus.Default);
_uploadProgressBarHeader.style.display = DisplayStyle.None;
_uploadProgressBarHeader.value = 0f;
_uploadProgressBarFlow.value = 0f;
_uploadProgressBarFlow.title = string.Empty;
}
private void EnableWorkflowElements(bool enable)
{
_workflowSelectionBox?.SetEnabled(enable);
_activeWorkflowElement?.SetEnabled(enable);
}
private void SetProgressBarColorByStatus(PackageUploadResult.UploadStatus status)
{
var color = PackageUploadResult.GetColorByStatus(status);
_uploadProgressFlowBg.style.backgroundColor = color;
_uploadProgressHeaderBg.style.backgroundColor = color;
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More