#if UNITY_EDITOR && (UNITY_2019_3_OR_NEWER || VALVE_UPDATE_FORCE)
using System.IO;
using System.Linq;
using Unity.XR.OpenVR.SimpleJSON;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEditor.PackageManager.UI;
using UnityEngine;
using UnityEngine.Networking;
namespace Unity.XR.OpenVR
{
[InitializeOnLoad]
public class OpenVRAutoUpdater : ScriptableObject
{
private const string valveOpenVRPackageStringOld = "com.valve.openvr";
private const string valveOpenVRPackageString = "com.valvesoftware.unity.openvr";
public const string npmRegistryStringValue = "\"name\": \"Valve\", \"url\": \"https://registry.npmjs.org\", \"scopes\": [ \"com.valvesoftware.unity.openvr\" ]";
public const string scopedRegisteryKey = "scopedRegistries";
private static ListRequest listRequest;
private static AddRequest addRequest;
private static RemoveRequest removeRequest;
private static SearchRequest searchRequest;
private static System.Diagnostics.Stopwatch packageTime = new System.Diagnostics.Stopwatch();
private const float estimatedTimeToInstall = 90; // in seconds
private const string updaterKeyTemplate = "com.valvesoftware.unity.openvr.updateState.{0}";
private static string updaterKey
{
get { return string.Format(updaterKeyTemplate, Application.productName); }
}
private static UpdateStates updateState
{
get { return _updateState; }
set
{
#if VALVE_DEBUG
Debug.Log("[DEBUG] Update State: " + value.ToString());
#endif
_updateState = value;
EditorPrefs.SetInt(updaterKey, (int)value);
}
}
private static UpdateStates _updateState = UpdateStates.Idle;
private static double runningSeconds
{
get
{
if (packageTime.IsRunning == false)
packageTime.Start();
return packageTime.Elapsed.TotalSeconds;
}
}
static OpenVRAutoUpdater()
{
#if UNITY_2020_1_OR_NEWER || VALVE_UPDATE_FORCE
Start();
#endif
}
public static void Start()
{
EditorApplication.update -= Update;
EditorApplication.update += Update;
}
///
/// State Machine
/// Idle: Start from last known state. If none is known go to request a removal of the current openvr package
/// WaitingForList: enumerate the packages to see if we have an existing package that needs to be removed. If so, request removal, if not, add scoped registry
/// WaitingForRemove: if the remove request has been nulled or completed successfully, request a list of packages for confirmation
/// WaitingForRemoveConfirmation: enumerate the packages and verify the removal succeeded. If it failed, try again.
/// If it succeeded, add the scoped registry.
/// WaitingForScopedRegistry: search for available packages until the openvr package is available. Then add the package
/// WaitingForAdd: if the add request has been nulled or completed successfully, request a list of packages for confirmation
/// WaitingForAddConfirmation: enumerate the packages and verify the add succeeded. If it failed, try again.
/// If it succeeded request removal of this script
/// RemoveSelf: delete the key that we've been using to maintain state. Delete this script and the containing folder if it's empty.
///
private static void Update()
{
switch (updateState)
{
case UpdateStates.Idle:
if (EditorPrefs.HasKey(updaterKey))
{
_updateState = (UpdateStates)EditorPrefs.GetInt(updaterKey);
packageTime.Start();
}
else
{
RequestList();
packageTime.Start();
}
break;
case UpdateStates.WaitingForList:
if (listRequest == null)
{
//the list request got nulled for some reason. Request it again.
RequestList();
}
else if (listRequest != null && listRequest.IsCompleted)
{
if (listRequest.Error != null || listRequest.Status == UnityEditor.PackageManager.StatusCode.Failure)
{
DisplayErrorAndStop("Error while checking for an existing openvr package.", listRequest);
}
else
{
if (listRequest.Result.Any(package => package.name == valveOpenVRPackageString))
{
//if it's there then remove it in preparation for adding the scoped registry
RequestRemove(valveOpenVRPackageString);
}
else if (listRequest.Result.Any(package => package.name == valveOpenVRPackageStringOld))
{
//if it's there then remove it in preparation for adding the scoped registry
RequestRemove(valveOpenVRPackageStringOld);
}
else
{
AddScopedRegistry();
}
}
}
else
{
if (runningSeconds > estimatedTimeToInstall)
{
DisplayErrorAndStop("Error while confirming package removal.", listRequest);
}
else
DisplayProgressBar();
}
break;
case UpdateStates.WaitingForRemove:
if (removeRequest == null)
{
//if our remove request was nulled out we should check if the package has already been removed.
RequestRemoveConfirmation();
}
else if (removeRequest != null && removeRequest.IsCompleted)
{
if (removeRequest.Error != null || removeRequest.Status == UnityEditor.PackageManager.StatusCode.Failure)
{
DisplayErrorAndStop("Error removing old version of OpenVR package.", removeRequest);
}
else
{
//verify that the package has been removed (then add)
RequestRemoveConfirmation();
}
}
else
{
if (packageTime.Elapsed.TotalSeconds > estimatedTimeToInstall)
DisplayErrorAndStop("Error removing old version of OpenVR package.", removeRequest);
else
DisplayProgressBar();
}
break;
case UpdateStates.WaitingForRemoveConfirmation:
if (listRequest == null)
{
//the list request got nulled for some reason. Request it again.
RequestRemoveConfirmation();
}
else if (listRequest != null && listRequest.IsCompleted)
{
if (listRequest.Error != null || listRequest.Status == UnityEditor.PackageManager.StatusCode.Failure)
{
DisplayErrorAndStop("Error while confirming package removal.", listRequest);
}
else
{
if (listRequest.Result.Any(package => package.name == valveOpenVRPackageString))
{
//try remove again if it didn't work and we don't know why.
RequestRemove(valveOpenVRPackageString);
}
else if (listRequest.Result.Any(package => package.name == valveOpenVRPackageStringOld))
{
//try remove again if it didn't work and we don't know why.
RequestRemove(valveOpenVRPackageStringOld);
}
else
{
AddScopedRegistry();
}
}
}
else
{
if (runningSeconds > estimatedTimeToInstall)
{
DisplayErrorAndStop("Error while confirming package removal.", listRequest);
}
else
DisplayProgressBar();
}
break;
case UpdateStates.WaitingForScopedRegistry:
if (searchRequest == null)
{
//the search request got nulled for some reason, request again
RequestScope();
}
else if (searchRequest != null && searchRequest.IsCompleted)
{
if (searchRequest.Error != null || searchRequest.Status == UnityEditor.PackageManager.StatusCode.Failure)
{
DisplayErrorAndStop("Error adding Valve scoped registry to project.", searchRequest);
}
RequestAdd();
}
else
{
if (packageTime.Elapsed.TotalSeconds > estimatedTimeToInstall)
DisplayErrorAndStop("Error while trying to add scoped registry.", addRequest);
else
DisplayProgressBar();
}
break;
case UpdateStates.WaitingForAdd:
if (addRequest == null)
{
//the add request got nulled for some reason. Request an add confirmation
RequestAddConfirmation();
}
else if (addRequest != null && addRequest.IsCompleted)
{
if (addRequest.Error != null || addRequest.Status == UnityEditor.PackageManager.StatusCode.Failure)
{
DisplayErrorAndStop("Error adding new version of OpenVR package.", addRequest);
}
else
{
//verify that the package has been added (then stop)
RequestAddConfirmation();
}
}
else
{
if (packageTime.Elapsed.TotalSeconds > estimatedTimeToInstall)
DisplayErrorAndStop("Error while trying to add package.", addRequest);
else
DisplayProgressBar();
}
break;
case UpdateStates.WaitingForAddConfirmation:
if (listRequest == null)
{
//the list request got nulled for some reason. Request it again.
RequestAddConfirmation();
}
else if (listRequest != null && listRequest.IsCompleted)
{
if (listRequest.Error != null || listRequest.Status == UnityEditor.PackageManager.StatusCode.Failure)
{
DisplayErrorAndStop("Error while confirming the OpenVR package has been added.", listRequest);
}
else
{
if (listRequest.Result.Any(package => package.name == valveOpenVRPackageString))
{
updateState = UpdateStates.RemoveSelf;
UnityEditor.EditorUtility.DisplayDialog("OpenVR", "OpenVR Unity XR successfully updated.", "Ok");
}
else
{
//try to add again if it's not there and we don't know why
RequestAdd();
}
}
}
else
{
if (runningSeconds > estimatedTimeToInstall)
{
DisplayErrorAndStop("Error while confirming the OpenVR package has been added.", listRequest);
}
else
DisplayProgressBar();
}
break;
case UpdateStates.RemoveSelf:
EditorPrefs.DeleteKey(updaterKey);
EditorUtility.ClearProgressBar();
EditorApplication.update -= Update;
#if VALVE_SKIP_DELETE
Debug.Log("[DEBUG] skipping script deletion. Complete.");
return;
#endif
var script = MonoScript.FromScriptableObject(OpenVRAutoUpdater.CreateInstance());
var path = AssetDatabase.GetAssetPath(script);
FileInfo updaterScript = new FileInfo(path);
FileInfo updaterScriptMeta = new FileInfo(path + ".meta");
FileInfo simpleJSONScript = new FileInfo(Path.Combine(updaterScript.Directory.FullName, "OpenVRSimpleJSON.cs"));
FileInfo simpleJSONScriptMeta = new FileInfo(Path.Combine(updaterScript.Directory.FullName, "OpenVRSimpleJSON.cs.meta"));
updaterScript.Delete();
updaterScriptMeta.Delete();
simpleJSONScript.Delete();
simpleJSONScriptMeta.Delete();
if (updaterScript.Directory.GetFiles().Length == 0 && updaterScript.Directory.GetDirectories().Length == 0)
{
path = updaterScript.Directory.FullName + ".meta";
updaterScript.Directory.Delete();
File.Delete(path);
}
AssetDatabase.Refresh();
break;
}
}
private const string packageManifestPath = "Packages/manifest.json";
private const string scopedRegistryKey = "scopedRegistries";
private const string scopedRegistryValue = "{ \"name\": \"Valve\",\n" +
"\"url\": \"https://registry.npmjs.org/\"," +
"\"scopes\": [" +
"\"com.valvesoftware\", \"com.valvesoftware.unity.openvr\"" +
"] }";
private const string scopedRegistryNodeTemplate = "[ {0} ]";
//load packages.json
//check for existing scoped registries
//check for our scoped registry
//if no to either then add it
//save file
//reload
private static void AddScopedRegistry()
{
if (File.Exists(packageManifestPath) == false)
{
Debug.LogError("[OpenVR Installer] Could not find package manifest at: " + packageManifestPath);
return;
}
bool needsSave = false;
string jsonText = File.ReadAllText(packageManifestPath);
JSONNode manifest = JSON.Parse(jsonText);
if (manifest.HasKey(scopedRegistryKey) == false)
{
manifest.Add(scopedRegistryKey, JSON.Parse(string.Format(scopedRegistryNodeTemplate, scopedRegistryValue)));
needsSave = true;
}
else
{
bool alreadyExists = false;
foreach (var scopedRegistry in manifest[scopedRegistryKey].AsArray)
{
if (scopedRegistry.Value != null && scopedRegistry.Value.HasKey("name") && scopedRegistry.Value["name"] == "Valve")
{
alreadyExists = true;
break;
}
}
if (alreadyExists == false)
{
manifest[scopedRegistryKey].Add(JSON.Parse(scopedRegistryValue));
needsSave = true;
}
}
if (needsSave)
{
File.WriteAllText(packageManifestPath, manifest.ToString(2));
Debug.Log("[OpenVR Installer] Wrote scoped registry file.");
}
RequestScope();
}
private static void RequestList()
{
updateState = UpdateStates.WaitingForList;
listRequest = Client.List();
}
private static void RequestRemove(string packageName)
{
updateState = UpdateStates.WaitingForRemove;
removeRequest = UnityEditor.PackageManager.Client.Remove(packageName);
}
private static void RequestAdd()
{
updateState = UpdateStates.WaitingForAdd;
addRequest = UnityEditor.PackageManager.Client.Add(valveOpenVRPackageString);
}
private static void RequestRemoveConfirmation()
{
updateState = UpdateStates.WaitingForRemoveConfirmation;
listRequest = Client.List();
}
private static void RequestAddConfirmation()
{
updateState = UpdateStates.WaitingForAddConfirmation;
listRequest = Client.List();
}
private static string dialogText = "Installing OpenVR Unity XR package from github using Unity Package Manager...";
private static void DisplayProgressBar()
{
bool cancel = UnityEditor.EditorUtility.DisplayCancelableProgressBar("SteamVR", dialogText, (float)packageTime.Elapsed.TotalSeconds / estimatedTimeToInstall);
if (cancel)
Stop();
}
private static void RequestScope()
{
searchRequest = Client.SearchAll(false);
updateState = UpdateStates.WaitingForScopedRegistry;
}
private static void DisplayErrorAndStop(string stepInfo, Request request)
{
string error = "";
if (request != null)
error = request.Error.message;
string errorMessage = string.Format("{0}:\n\t{1}\n\nPlease manually reinstall the package through the package manager.", stepInfo, error);
UnityEngine.Debug.LogError(errorMessage);
Stop();
UnityEditor.EditorUtility.DisplayDialog("OpenVR Error", errorMessage, "Ok");
}
private static void Stop()
{
updateState = UpdateStates.RemoveSelf;
}
private enum UpdateStates
{
Idle,
WaitingForList,
WaitingForRemove,
WaitingForRemoveConfirmation,
WaitingForScopedRegistry,
WaitingForAdd,
WaitingForAddConfirmation,
RemoveSelf,
}
}
}
#endif