241 lines
8.3 KiB
C#
241 lines
8.3 KiB
C#
using System;
|
||
using System.Globalization;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Semver;
|
||
using UnityEngine;
|
||
using UnityEditor.PackageManager.Requests;
|
||
|
||
namespace UnityEditor.PackageManager.UI
|
||
{
|
||
internal abstract class UpmBaseOperation : IBaseOperation
|
||
{
|
||
private static readonly string[] s_WhitelistedUnityPackages =
|
||
{
|
||
"com.havok.physics",
|
||
"com.ptc.vuforia.engine"
|
||
};
|
||
|
||
public static string GroupName(PackageSource origin)
|
||
{
|
||
var group = PackageGroupOrigins.Packages.ToString();
|
||
if (origin == PackageSource.BuiltIn)
|
||
group = PackageGroupOrigins.BuiltInPackages.ToString();
|
||
|
||
return group;
|
||
}
|
||
|
||
protected static IEnumerable<PackageInfo> FromUpmPackageInfo(PackageManager.PackageInfo info, bool isCurrent = true)
|
||
{
|
||
var packages = new List<PackageInfo>();
|
||
var displayName = info.displayName;
|
||
if (string.IsNullOrEmpty(displayName))
|
||
{
|
||
displayName = info.name.Replace("com.unity.modules.", "");
|
||
if (displayName.StartsWith("com.")) displayName = displayName.Replace("com.", "");
|
||
if (displayName.StartsWith("unity.")) displayName = displayName.Replace("unity.", "");
|
||
displayName = new CultureInfo("en-US").TextInfo.ToTitleCase(displayName);
|
||
}
|
||
|
||
string author = info.author.name;
|
||
if (info.name.StartsWith("com.unity.") || s_WhitelistedUnityPackages.Any(name => info.name == name))
|
||
author = "Unity Technologies Inc.";
|
||
if (string.IsNullOrEmpty(author))
|
||
author = "Other";
|
||
|
||
var lastCompatible = info.versions.latestCompatible;
|
||
var versions = new List<string>();
|
||
versions.AddRange(info.versions.compatible);
|
||
if (versions.FindIndex(version => version == info.version) == -1)
|
||
{
|
||
versions.Add(info.version);
|
||
|
||
versions.Sort((left, right) =>
|
||
{
|
||
if (left == null || right == null) return 0;
|
||
|
||
SemVersion leftVersion = left;
|
||
SemVersion righVersion = right;
|
||
return leftVersion.CompareByPrecedence(righVersion);
|
||
});
|
||
|
||
SemVersion packageVersion = info.version;
|
||
if (!string.IsNullOrEmpty(lastCompatible))
|
||
{
|
||
SemVersion lastCompatibleVersion =
|
||
string.IsNullOrEmpty(lastCompatible) ? (SemVersion)null : lastCompatible;
|
||
if (packageVersion != null && string.IsNullOrEmpty(packageVersion.Prerelease) &&
|
||
packageVersion.CompareByPrecedence(lastCompatibleVersion) > 0)
|
||
lastCompatible = info.version;
|
||
}
|
||
else
|
||
{
|
||
if (packageVersion != null && string.IsNullOrEmpty(packageVersion.Prerelease))
|
||
lastCompatible = info.version;
|
||
}
|
||
}
|
||
|
||
foreach (var version in versions)
|
||
{
|
||
var isVersionCurrent = version == info.version && isCurrent;
|
||
var isBuiltIn = info.source == PackageSource.BuiltIn;
|
||
var isVerified = string.IsNullOrEmpty(SemVersion.Parse(version).Prerelease) && version == info.versions.recommended;
|
||
var isUnityPackage = info.name.StartsWith("com.unity.") || s_WhitelistedUnityPackages.Any(name => info.name == name);
|
||
var state = (isBuiltIn || info.version == lastCompatible || !isCurrent) ? PackageState.UpToDate : PackageState.Outdated;
|
||
|
||
// Happens mostly when using a package that hasn't been in production yet.
|
||
if (info.versions.all.Length <= 0)
|
||
state = PackageState.UpToDate;
|
||
|
||
if (info.errors.Length > 0)
|
||
state = PackageState.Error;
|
||
|
||
var packageInfo = new PackageInfo
|
||
{
|
||
Name = info.name,
|
||
DisplayName = displayName,
|
||
PackageId = version == info.version ? info.packageId : null,
|
||
Version = version,
|
||
Description = info.description,
|
||
Category = info.category,
|
||
IsCurrent = isVersionCurrent,
|
||
IsLatest = version == lastCompatible,
|
||
IsVerified = isVerified,
|
||
Errors = info.errors.ToList(),
|
||
Group = GroupName(info.source),
|
||
State = state,
|
||
Origin = isBuiltIn || isVersionCurrent ? info.source : PackageSource.Registry,
|
||
Author = author,
|
||
Info = info,
|
||
IsUnityPackage = isUnityPackage
|
||
};
|
||
|
||
packages.Add(packageInfo);
|
||
}
|
||
|
||
return packages;
|
||
}
|
||
|
||
public static event Action<UpmBaseOperation> OnOperationStart = delegate {};
|
||
|
||
public event Action<Error> OnOperationError = delegate {};
|
||
public event Action OnOperationFinalized = delegate {};
|
||
|
||
public Error ForceError { get; set; } // Allow external component to force an error on the requests (eg: testing)
|
||
public Error Error { get; protected set; } // Keep last error
|
||
|
||
public bool IsCompleted { get; private set; }
|
||
|
||
protected abstract Request CreateRequest();
|
||
|
||
[SerializeField]
|
||
protected Request CurrentRequest;
|
||
public readonly ThreadedDelay Delay = new ThreadedDelay();
|
||
|
||
protected abstract void ProcessData();
|
||
|
||
protected void Start()
|
||
{
|
||
Error = null;
|
||
OnOperationStart(this);
|
||
|
||
Delay.Start();
|
||
|
||
if (TryForcedError())
|
||
return;
|
||
|
||
EditorApplication.update += Progress;
|
||
}
|
||
|
||
// Common progress code for all classes
|
||
private void Progress()
|
||
{
|
||
if (!Delay.IsDone)
|
||
return;
|
||
|
||
// Create the request after the delay
|
||
if (CurrentRequest == null)
|
||
{
|
||
CurrentRequest = CreateRequest();
|
||
}
|
||
|
||
// Since CurrentRequest's error property is private, we need to simulate
|
||
// an error instead of just setting it.
|
||
if (TryForcedError())
|
||
return;
|
||
|
||
if (CurrentRequest.IsCompleted)
|
||
{
|
||
if (CurrentRequest.Status == StatusCode.Success)
|
||
OnDone();
|
||
else if (CurrentRequest.Status >= StatusCode.Failure)
|
||
OnError(CurrentRequest.Error);
|
||
else
|
||
Debug.LogError("Unsupported progress state " + CurrentRequest.Status);
|
||
}
|
||
}
|
||
|
||
private void OnError(Error error)
|
||
{
|
||
try
|
||
{
|
||
Error = error;
|
||
|
||
var message = "Cannot perform upm operation.";
|
||
if (error != null)
|
||
message = "Cannot perform upm operation: " + Error.message + " [" + Error.errorCode + "]";
|
||
|
||
Debug.LogError(message);
|
||
|
||
OnOperationError(Error);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Debug.LogError("Package Manager Window had an error while reporting an error in an operation: " + exception);
|
||
}
|
||
|
||
FinalizeOperation();
|
||
}
|
||
|
||
private void OnDone()
|
||
{
|
||
try
|
||
{
|
||
ProcessData();
|
||
}
|
||
catch (Exception error)
|
||
{
|
||
Debug.LogError("Package Manager Window had an error while completing an operation: " + error);
|
||
}
|
||
|
||
FinalizeOperation();
|
||
}
|
||
|
||
private void FinalizeOperation()
|
||
{
|
||
EditorApplication.update -= Progress;
|
||
OnOperationFinalized();
|
||
IsCompleted = true;
|
||
}
|
||
|
||
public void Cancel()
|
||
{
|
||
EditorApplication.update -= Progress;
|
||
OnOperationError = delegate {};
|
||
OnOperationFinalized = delegate {};
|
||
IsCompleted = true;
|
||
}
|
||
|
||
private bool TryForcedError()
|
||
{
|
||
if (ForceError != null)
|
||
{
|
||
OnError(ForceError);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|
||
}
|