Created
November 12, 2023 14:05
-
-
Save Thaina/eec5752b25f7bfd3737f7dd9ed2fa53c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEditor; | |
using UnityEditor.PackageManager; | |
// require package com.unity.editorcoroutines | |
using Unity.EditorCoroutines.Editor; | |
public class PackageMissingManagerWizard : ScriptableWizard | |
{ | |
static int mainProgress = -1; | |
static PackageMissingManagerWizard instance; | |
static Progress.Status? MainProgressStatus => mainProgress != -1 && Progress.Exists(mainProgress) ? Progress.GetStatus(mainProgress) : null; | |
static bool MainProgressAvailable => MainProgressStatus is not null or Progress.Status.Canceled or Progress.Status.Failed; | |
[InitializeOnLoadMethod] | |
static void Initial() | |
{ | |
if(mainProgress < 0) | |
mainProgress = Progress.Start("Start missing dependencies resolver",null,Progress.Options.Sticky); | |
EditorCoroutineUtility.StartCoroutineOwnerless(DoWork()); | |
static IEnumerator DoWork() | |
{ | |
var itor = WorkOnProgress(); | |
while(itor.MoveNext()) | |
{ | |
if(!MainProgressAvailable) | |
yield break; | |
yield return itor.Current; | |
} | |
yield return new YieldInstruction(); | |
if(MainProgressStatus is Progress.Status.Running or Progress.Status.Paused) | |
{ | |
Debug.Log(MainProgressStatus); | |
Progress.Finish(mainProgress,Progress.Status.Failed); | |
} | |
if(instance) | |
{ | |
instance.Close(); | |
instance = null; | |
} | |
} | |
} | |
static IEnumerator WorkOnProgress() | |
{ | |
var list = Client.List(false,true); | |
while(!list.IsCompleted) | |
{ | |
Progress.SetDescription(mainProgress,"Listing project dependencies"); | |
yield return new YieldInstruction(); | |
} | |
yield return string.Join("\n",new object[] { list.Status,list.Error }.Where((obj) => obj != null)); | |
var allMissings = list.Result.SelectMany((info) => { | |
return info.dependencies.Where((dep) => !list.Result.Any((item) => item.name == dep.name)); | |
}).GroupBy((info) => info.name,(info) => info.version).Where((group) => group.Any()).Select((group) => { | |
return new MissingDependency() { | |
Name = group.Key, | |
Versions = group.ToArray() | |
}; | |
}).ToArray(); | |
if(allMissings.Length < 1) | |
{ | |
Progress.Remove(mainProgress); | |
yield break; | |
} | |
if(!instance) | |
instance = ScriptableWizard.DisplayWizard<PackageMissingManagerWizard>("Sort missing dependency for resolving","Apply","Cancel"); | |
instance.missingDependencies = allMissings; | |
instance.Focus(); | |
var task = instance.source.Task; | |
while(!task.IsCompleted) | |
{ | |
Progress.SetDescription(mainProgress,"Wait for accept"); | |
yield return new YieldInstruction(); | |
} | |
if(task.IsCanceled || !(task.Result is var missingDependencies && missingDependencies?.Length > 0)) | |
{ | |
Progress.Remove(mainProgress); | |
yield break; | |
} | |
var missingWithProgresses = missingDependencies.Select((missing) => { | |
return (missing,Progress.Start(missing.Name,null,Progress.Options.Sticky,mainProgress)); | |
}).ToArray(); | |
int length = missingWithProgresses.Length; | |
Progress.Report(mainProgress,0,length,"Resolving missing dependencies"); | |
var resolveItor = DoWork(missingWithProgresses); | |
while(resolveItor.MoveNext()) | |
{ | |
Progress.Report(mainProgress,resolveItor.Current,length,"Resolving missing dependencies"); | |
yield return new WaitForSecondsRealtime(0.1f); | |
} | |
yield return new YieldInstruction(); | |
Progress.Finish(mainProgress,Progress.Status.Succeeded); | |
yield return new YieldInstruction(); | |
Debug.Log(MainProgressStatus); | |
} | |
static IEnumerator<int> DoWork((MissingDependency missing, int progressID)[] missingWithProgresses) | |
{ | |
int i = 0; | |
foreach(var (missing,progressID) in missingWithProgresses) | |
{ | |
var lastStatus = StatusCode.InProgress; | |
foreach(var (version,index) in missing.Versions.Select((item,index) => (item,index))) | |
{ | |
int retry = 1; | |
while(retry > 0 || lastStatus == StatusCode.Success) | |
{ | |
retry--; | |
var add = Client.Add(version); | |
while(!add.IsCompleted) | |
{ | |
Progress.Report(progressID,index,missing.Versions.Length,"try add dependency"); | |
yield return i; | |
} | |
lastStatus = add.Status; | |
if(add.Error != null) | |
{ | |
Debug.LogErrorFormat("{0} : {1}",add.Error.errorCode,add.Error.message); | |
if(add.Error.errorCode == ErrorCode.Unknown && add.Error.message.Contains("EBUSY")) | |
retry = 3; | |
} | |
} | |
if(lastStatus == StatusCode.Success) | |
break; | |
} | |
Progress.SetDescription(progressID,lastStatus.ToString()); | |
Progress.Finish(progressID,lastStatus == StatusCode.Success ? Progress.Status.Succeeded : Progress.Status.Failed); | |
if(lastStatus == StatusCode.Success) | |
i++; | |
} | |
yield return i; | |
} | |
[Serializable] | |
public struct MissingDependency | |
{ | |
public string Name; | |
public string[] Versions; | |
} | |
[SerializeField] | |
MissingDependency[] missingDependencies; | |
TaskCompletionSource<MissingDependency[]> source = new TaskCompletionSource<MissingDependency[]>(); | |
private void OnDestroy() => source.TrySetCanceled(); | |
private void OnWizardCreate() => source.TrySetResult(missingDependencies); | |
private void OnWizardOtherButton() => source.TrySetResult(null); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment