253 lines
9.5 KiB
C#
253 lines
9.5 KiB
C#
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the Oculus SDK License Agreement (the "License");
|
|
* you may not use the Oculus SDK except in compliance with the License,
|
|
* which is provided at the time of installation or download, or which
|
|
* otherwise accompanies this software in either electronic or hard copy form.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://developer.oculus.com/licenses/oculussdk/
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Threading.Tasks;
|
|
using UnityEditor;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace Meta.XR.Simulator.Editor
|
|
{
|
|
internal static class Installer
|
|
{
|
|
private const string Name = "Meta XR Simulator Installer";
|
|
|
|
private static bool? _isInstalled;
|
|
public static bool IsInstalled => _isInstalled ??= Directory.Exists(XRSimConstants.XrSimDataFolderPath);
|
|
public static event Action OnInstalled;
|
|
|
|
private static UnityWebRequest _webRequest = null;
|
|
|
|
#if UNITY_EDITOR_OSX
|
|
private const string DLLName = "SIMULATOR.so";
|
|
#else
|
|
private const string DLLName = "SIMULATOR.dll";
|
|
#endif
|
|
private static string _dllPath;
|
|
private static string DllPath => _dllPath ??= XRSimConstants.XrSimDataFolderPath == null ? null : Path.GetFullPath(Path.Join(XRSimConstants.XrSimDataFolderPath, DLLName));
|
|
|
|
public static bool IsSimulatorInstalled()
|
|
{
|
|
return !string.IsNullOrEmpty(XRSimConstants.JsonPath) &&
|
|
!string.IsNullOrEmpty(DllPath) &&
|
|
Utils.SystemUtils.FileExists(XRSimConstants.JsonPath) &&
|
|
Utils.SystemUtils.FileExists(DllPath);
|
|
}
|
|
|
|
public static async Task<bool> EnsureMetaXRSimulatorInstalled()
|
|
{
|
|
|
|
if (IsSimulatorInstalled())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
|
return await InstallXRSim(XRSimConstants.XrSimDataFolderPath, () =>
|
|
{
|
|
_isInstalled = true;
|
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
|
OnInstalled?.Invoke();
|
|
}, errorMessage =>
|
|
{
|
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
|
Utils.LogUtils.DisplayDialogOrError(Name, errorMessage, true);
|
|
});
|
|
}
|
|
|
|
private static void OnPlayModeStateChanged(PlayModeStateChange state)
|
|
{
|
|
if (state == PlayModeStateChange.ExitingEditMode)
|
|
{
|
|
// Cancel entering play mode
|
|
UnityEngine.Debug.LogError($"Synthetic Environments installation is in progress. Please wait until is finished.");
|
|
EditorApplication.isPlaying = false;
|
|
}
|
|
}
|
|
|
|
internal static async Task<bool> InstallXRSim(string installDir, Action onSuccess, Action<string> onError)
|
|
{
|
|
if (XRSimConstants.DownloadFolderPath == null)
|
|
{
|
|
onError?.Invoke($"Package is not initialised. Please try again in a few minutes.");
|
|
return false;
|
|
}
|
|
|
|
if (!await DownloadPackage(XRSimConstants.DownloadFolderPath, errorMessage =>
|
|
{
|
|
onError?.Invoke(errorMessage);
|
|
}))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!await Task.Run(() => UnzipPackage(XRSimConstants.DownloadFolderPath, installDir, s =>
|
|
{
|
|
onError?.Invoke(s);
|
|
})))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Utils.LogUtils.ReportInfo(Name, "finished extracting " + XRSimConstants.DownloadFolderPath + " to " + installDir);
|
|
onSuccess?.Invoke();
|
|
return true;
|
|
}
|
|
|
|
internal static async Task<bool> DownloadPackage(string downloadPath, Action<string> onError)
|
|
{
|
|
// check if file exists before downloading it again
|
|
if (Utils.SystemUtils.FileExists(downloadPath))
|
|
{
|
|
Utils.LogUtils.ReportInfo(Name, "Found " + downloadPath + ", skipping download");
|
|
return true;
|
|
}
|
|
|
|
string url = Utils.SystemUtils.GetDownloadURL();
|
|
|
|
int progressId = Utils.LogUtils.CreateProgress(Name, false);
|
|
|
|
_webRequest = UnityWebRequest.Get(url);
|
|
var handler = new DownloadHandlerFile(downloadPath);
|
|
_webRequest.downloadHandler = handler;
|
|
handler.removeFileOnAbort = true;
|
|
|
|
UnityWebRequestAsyncOperation operation = _webRequest.SendWebRequest();
|
|
operation.completed += _ =>
|
|
{
|
|
Utils.LogUtils.ReportInfo(Name, "finished downloading " + url);
|
|
};
|
|
|
|
while (!_webRequest.downloadHandler.isDone && !operation.isDone)
|
|
{
|
|
Progress.Report(progressId, _webRequest.downloadProgress, "Downloading package");
|
|
await Task.Yield();
|
|
}
|
|
|
|
if (_webRequest.result != UnityWebRequest.Result.Success)
|
|
{
|
|
onError?.Invoke(_webRequest.error);
|
|
Progress.Finish(progressId, Progress.Status.Failed);
|
|
return false;
|
|
}
|
|
|
|
Utils.LogUtils.ReportInfo(Name, "Finished saving data to " + downloadPath);
|
|
_webRequest = null;
|
|
Progress.Remove(progressId);
|
|
|
|
#if UNITY_EDITOR_OSX
|
|
// Remove quarantine attribute from downloaded file
|
|
{
|
|
const string Attribute = "com.apple.provenance";
|
|
var args = Utils.EscapeArguments(new string[] { "-d", Attribute, downloadPath });
|
|
var (retCode, contents) = Utils.ProcessUtils.ExecuteProcess("xattr", args);
|
|
if(retCode != 0)
|
|
{
|
|
Utils.LogUtils.ReportError(Name, string.Format("failed to remove {0}, retCode={1}, contents={2}", Attribute, retCode, contents));
|
|
}
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
internal static bool UnzipPackage(string downloadPath, string installDir, Action<string> onError)
|
|
{
|
|
// unzip
|
|
ZipArchive archive;
|
|
try
|
|
{
|
|
archive = ZipFile.OpenRead(downloadPath);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
onError?.Invoke(e.Message);
|
|
return false;
|
|
}
|
|
|
|
// ensure normalized path
|
|
if (!installDir.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
|
|
{
|
|
installDir += Path.DirectorySeparatorChar;
|
|
}
|
|
|
|
if (Directory.Exists(installDir))
|
|
{
|
|
// Ensure directory is deleted before extracting
|
|
Directory.Delete(installDir, true);
|
|
}
|
|
|
|
foreach (ZipArchiveEntry entry in archive.Entries)
|
|
{
|
|
if (entry.FullName.EndsWith("/"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Gets the full path to ensure that relative segments are removed.
|
|
string destinationPath = Path.GetFullPath(Path.Combine(installDir, entry.FullName));
|
|
|
|
// Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
|
|
// are case-insensitive.
|
|
if (!destinationPath.StartsWith(installDir, StringComparison.Ordinal))
|
|
{
|
|
continue;
|
|
}
|
|
// create directory if it doesn't exist
|
|
var parentDir = Path.GetDirectoryName(destinationPath);
|
|
if (!Directory.Exists(parentDir))
|
|
{
|
|
Directory.CreateDirectory(parentDir);
|
|
}
|
|
|
|
entry.ExtractToFile(destinationPath, true);
|
|
|
|
#if UNITY_EDITOR_OSX
|
|
// Update the file attributes for file
|
|
string subString = "";
|
|
int extendedAttributes = entry.ExternalAttributes >> 16;
|
|
if(extendedAttributes != 0) {
|
|
string attrString = Convert.ToString(extendedAttributes, 8);
|
|
subString = attrString.Substring(attrString.Length - 4);
|
|
} else if(entry.FullName.Contains("SyntheticEnvironmentServer")) {
|
|
// This is a hack to set the executable bit on the SyntheticEnvironmentServer binary
|
|
subString = "750";
|
|
}
|
|
|
|
if(subString.Length > 0) {
|
|
Utils.LogUtils.ReportInfo(Name, "setting permissions on " + destinationPath + " to " + subString);
|
|
var args = Utils.EscapeArguments(new string[] { subString, destinationPath });
|
|
var (retCode, contents) = Utils.ProcessUtils.ExecuteProcess("chmod", args);
|
|
if(retCode != 0)
|
|
{
|
|
Utils.LogUtils.ReportError(Name, "failed to set permissions on " + destinationPath + ", retCode:" + retCode + ", contents:" + contents);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
archive.Dispose();
|
|
return true;
|
|
}
|
|
}
|
|
}
|