Heroes_of_Hiis/Assets/Oculus/VR/Editor/OVRPlatformTool.cs

1354 lines
44 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
namespace Assets.Oculus.VR.Editor
{
public class OVRPlatformTool : EditorWindow
{
public enum TargetPlatform
{
Rift = 0,
//OculusGoGearVR = 1, // DEPRECATED
Quest = 2,
None = 3,
};
public enum GamepadType
{
OFF,
TWINSTICK,
RIGHT_D_PAD,
LEFT_D_PAD,
};
const string urlPlatformUtil =
"https://www.oculus.com/download_app/?id=1076686279105243";
static private Process ovrPlatUtilProcess;
Vector2 commandMenuScroll;
Vector2 debugLogScroll;
static public string log;
private static bool activeProcess = false;
private static bool ranSelfUpdate = false;
private static int retryCount = 0;
private static string appToken;
private const float buttonPadding = 5.0f;
private bool showOptionalCommands = false;
private bool show2DCommands = false;
private bool showExpansionFileCommands = false;
private bool showRedistCommands = false;
private bool showUploadDebugSymbols = false;
private const float INDENT_SPACING = 15f;
private const float SINGLE_LINE_SPACING = 18f;
private const float ASSET_CONFIG_BACKGROUND_PADDING = 10f;
private const float DEFAULT_LABEL_WIDTH = 180f;
private const int MAX_DOWNLOAD_RETRY_COUNT = 2;
private static GUIStyle boldFoldoutStyle;
private static GUIStyle odhCalloutStyle;
[MenuItem("Oculus/Tools/Oculus Platform Tool")]
static void Init()
{
OVRPlatformTool.log = string.Empty;
// Get existing open window or if none, make a new one:
EditorWindow.GetWindow(typeof(OVRPlatformTool));
// Populate initial target platform value based on OVRDeviceSelector
#if UNITY_ANDROID
if (OVRDeviceSelector.isTargetDeviceQuestFamily)
{
OVRPlatformToolSettings.TargetPlatform = TargetPlatform.Quest;
}
#else
OVRPlatformToolSettings.TargetPlatform = TargetPlatform.Rift;
#endif
// Load redist packages by calling list-redists in the CLI
string dataPath = Application.dataPath;
var thread = new Thread(delegate () {
retryCount = 0;
LoadRedistPackages(dataPath);
});
thread.Start();
OVRPlugin.SendEvent("oculus_platform_tool", "show_window");
}
void OnGUI()
{
if (boldFoldoutStyle == null)
{
boldFoldoutStyle = new GUIStyle(EditorStyles.foldout);
boldFoldoutStyle.fontStyle = FontStyle.Bold;
}
if (odhCalloutStyle == null)
{
odhCalloutStyle = new GUIStyle(EditorStyles.label);
odhCalloutStyle.richText = true;
odhCalloutStyle.wordWrap = true;
}
EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH;
GUILayout.Label("OVR Platform Tool", EditorStyles.boldLabel);
this.titleContent.text = "OVR Platform Tool";
GUIContent TargetPlatformLabel = new GUIContent("Target Oculus Platform");
OVRPlatformToolSettings.TargetPlatform = (TargetPlatform)MakeEnumPopup(TargetPlatformLabel, OVRPlatformToolSettings.TargetPlatform);
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.None)
return;
SetOVRProjectConfig(OVRPlatformToolSettings.TargetPlatform);
SetDirtyOnGUIChange();
commandMenuScroll = EditorGUILayout.BeginScrollView(commandMenuScroll, GUILayout.Height(Screen.height / 2));
{
// Add the UI Form
EditorGUI.BeginChangeCheck();
GUILayout.Space(15.0f);
// App ID
GUIContent AppIDLabel = new GUIContent("Oculus Application ID [?]: ",
"This AppID will be used when uploading the build.");
OVRPlatformToolSettings.AppID = MakeTextBox(AppIDLabel, OVRPlatformToolSettings.AppID);
// App Token
GUIContent AppTokenLabel = new GUIContent("Oculus App Token [?]: ",
"You can get your app token from your app's Oculus API Dashboard.");
appToken = MakePasswordBox(AppTokenLabel, appToken);
// Release Channel
GUIContent ReleaseChannelLabel = new GUIContent("Release Channel [?]: ",
"Specify the releaes channel of the new build, you can reassign to other channels after upload.");
OVRPlatformToolSettings.ReleaseChannel = MakeTextBox(ReleaseChannelLabel, OVRPlatformToolSettings.ReleaseChannel);
// Releaes Note
GUIContent ReleaseNoteLabel = new GUIContent("Release Note: ");
OVRPlatformToolSettings.ReleaseNote = MakeTextBox(ReleaseNoteLabel, OVRPlatformToolSettings.ReleaseNote);
// Platform specific fields
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Rift)
{
GUIContent BuildDirLabel = new GUIContent("Rift Build Directory [?]: ",
"The full path to the directory containing your Rift build files.");
OVRPlatformToolSettings.RiftBuildDirectory = MakeFileDirectoryField(BuildDirLabel, OVRPlatformToolSettings.RiftBuildDirectory,
"Choose Rifle Build Directory");
GUIContent BuildVersionLabel = new GUIContent("Build Version [?]: ",
"The version number shown to users.");
OVRPlatformToolSettings.RiftBuildVersion = MakeTextBox(BuildVersionLabel, OVRPlatformToolSettings.RiftBuildVersion);
GUIContent LaunchFileLabel = new GUIContent("Launch File Path [?]: ",
"The full path to the executable that launches your app.");
OVRPlatformToolSettings.RiftLaunchFile = MakeFileDirectoryField(LaunchFileLabel, OVRPlatformToolSettings.RiftLaunchFile,
"Choose Launch File", true, "exe");
}
else
{
GUIContent ApkPathLabel = new GUIContent("Build APK File Path [?]: ",
"The full path to the APK file.");
OVRPlatformToolSettings.ApkBuildPath = MakeFileDirectoryField(ApkPathLabel, OVRPlatformToolSettings.ApkBuildPath,
"Choose APK File", true, "apk");
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
{
// Quest specific fields
}
}
GUIContent uploadDebugSymbolsLabel = new GUIContent("Upload Debug Symbols [?]: ",
"Check this box to enable uploading of debug symbols.");
OVRPlatformToolSettings.UploadDebugSymbols = MakeToggleBox(uploadDebugSymbolsLabel, OVRPlatformToolSettings.UploadDebugSymbols);
if (OVRPlatformToolSettings.UploadDebugSymbols)
{
IncrementIndent();
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
{
if (showUploadDebugSymbols != OVRPlatformToolSettings.UploadDebugSymbols)
{
// If no debug symbol directory is set, default to the expected place based of scripting backend
if (string.IsNullOrEmpty(OVRPlatformToolSettings.DebugSymbolsDirectory))
{
ScriptingImplementation scriptingBackend = PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup);
if (scriptingBackend == ScriptingImplementation.IL2CPP)
{
OVRPlatformToolSettings.DebugSymbolsDirectory = Path.Combine(Application.dataPath, "../Temp/StagingArea/symbols");
}
else
{
OVRPlatformToolSettings.DebugSymbolsDirectory = Path.Combine(EditorApplication.applicationContentsPath,
"PlaybackEngines/AndroidPlayer/Variations/mono/Release/Symbols/armeabi-v7a");
}
}
EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
}
GUIContent DebugSymbolLabel = new GUIContent("Debug Symbols Directory [?]: ",
"The full path to the directory containing the app symbols (libil2cpp.sym.so)");
OVRPlatformToolSettings.DebugSymbolsDirectory = MakeFileDirectoryField(DebugSymbolLabel, OVRPlatformToolSettings.DebugSymbolsDirectory,
"Choose Debug Symbols Directory", false);
GUIContent uploadSymbolsOnlyLabel = new GUIContent("Upload Debug Symbols Only [?]: ",
"Check this box to upload debug symbols to an existing build. Will require a Build ID along with the App ID, App Token, and Symbols Directory.");
OVRPlatformToolSettings.UploadDebugSymbolsOnly = MakeToggleBox(uploadSymbolsOnlyLabel, OVRPlatformToolSettings.UploadDebugSymbolsOnly);
if (OVRPlatformToolSettings.UploadDebugSymbolsOnly)
{
GUIContent BuildIDLabel = new GUIContent("Build ID [?]: ",
"This BuildID will be used for uploading debug symbols to a specific build.");
OVRPlatformToolSettings.BuildID = MakeTextBox(BuildIDLabel, OVRPlatformToolSettings.BuildID);
}
}
DecrementIndent();
}
showUploadDebugSymbols = OVRPlatformToolSettings.UploadDebugSymbols;
showOptionalCommands = EditorGUILayout.Foldout(showOptionalCommands, "Optional Commands", boldFoldoutStyle);
if (showOptionalCommands)
{
IncrementIndent();
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Rift)
{
// Launch Parameters
GUIContent LaunchParamLabel = new GUIContent("Launch Parameters [?]: ",
"Specifies any arguments passed to the launcher.");
OVRPlatformToolSettings.RiftLaunchParams = MakeTextBox(LaunchParamLabel, OVRPlatformToolSettings.RiftLaunchParams);
GUIContent FirewallExceptionLabel = new GUIContent("Firewall Exception [?]: ",
"Specifies if a Windows Firewall exception is required.");
OVRPlatformToolSettings.RiftFirewallException = MakeToggleBox(FirewallExceptionLabel, OVRPlatformToolSettings.RiftFirewallException);
GUIContent GamepadEmulationLabel = new GUIContent("Gamepad Emulation [?]: ",
"Specifies the type of gamepad emulation used by the Oculus Touch controllers.");
OVRPlatformToolSettings.RiftGamepadEmulation = (GamepadType)MakeEnumPopup(GamepadEmulationLabel, OVRPlatformToolSettings.RiftGamepadEmulation);
show2DCommands = EditorGUILayout.Foldout(show2DCommands, "2D", boldFoldoutStyle);
if (show2DCommands)
{
IncrementIndent();
// 2D Launch File
GUIContent LaunchFile2DLabel = new GUIContent("2D Launch File [?]: ",
"The full path to the executable that launches your app in 2D mode.");
OVRPlatformToolSettings.Rift2DLaunchFile = MakeFileDirectoryField(LaunchFile2DLabel, OVRPlatformToolSettings.Rift2DLaunchFile,
"Choose 2D Launch File", true, "exe");
// 2D Launch Parameters
GUIContent LaunchParam2DLabel = new GUIContent("2D Launch Parameters [?]: ",
"Specifies any arguments passed to the launcher in 2D mode.");
OVRPlatformToolSettings.Rift2DLaunchParams = MakeTextBox(LaunchParam2DLabel, OVRPlatformToolSettings.Rift2DLaunchParams);
DecrementIndent();
}
showRedistCommands = EditorGUILayout.Foldout(showRedistCommands, "Redistributable Packages", boldFoldoutStyle);
if (showRedistCommands)
{
IncrementIndent();
for (int i = 0; i < OVRPlatformToolSettings.RiftRedistPackages.Count; i++)
{
GUIContent RedistPackageLabel = new GUIContent(OVRPlatformToolSettings.RiftRedistPackages[i].name);
OVRPlatformToolSettings.RiftRedistPackages[i].include = MakeToggleBox(RedistPackageLabel, OVRPlatformToolSettings.RiftRedistPackages[i].include);
}
DecrementIndent();
}
showExpansionFileCommands = EditorGUILayout.Foldout(showExpansionFileCommands, "Expansion Files", boldFoldoutStyle);
if (showExpansionFileCommands)
{
IncrementIndent();
// Language Pack Directory
GUIContent LanguagePackLabel = new GUIContent("Language Pack Directory [?]: ",
"The full path to the directory containing the language packs");
OVRPlatformToolSettings.LanguagePackDirectory = MakeFileDirectoryField(LanguagePackLabel, OVRPlatformToolSettings.LanguagePackDirectory,
"Choose Language Pack Directory");
}
}
else
{
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
{
// Quest specific optional fields
}
showExpansionFileCommands = EditorGUILayout.Foldout(showExpansionFileCommands, "Expansion Files", boldFoldoutStyle);
if (showExpansionFileCommands)
{
IncrementIndent();
// OBB File Path
GUIContent ObbPathLabel = new GUIContent("OBB File Path [?]: ",
"The full path to the OBB file.");
OVRPlatformToolSettings.ObbFilePath = MakeFileDirectoryField(ObbPathLabel, OVRPlatformToolSettings.ObbFilePath,
"Choose OBB File", true, "obb");
}
}
if (showExpansionFileCommands)
{
// Assets Directory
GUIContent AssetsDirLabel = new GUIContent("Assets Directory [?]: ",
"The full path to the directory with DLCs for this build.");
string assetsDirectory = MakeFileDirectoryField(AssetsDirLabel, OVRPlatformToolSettings.AssetsDirectory,
"Choose Assets Directory");
if (assetsDirectory != OVRPlatformToolSettings.AssetsDirectory)
{
OVRPlatformToolSettings.AssetsDirectory = assetsDirectory;
OVRPlatformToolSettings.AssetConfigs.Clear();
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.AssetsDirectory))
{
DirectoryInfo dirInfo = new DirectoryInfo(OVRPlatformToolSettings.AssetsDirectory);
FileInfo[] assetFiles = dirInfo.GetFiles();
foreach (FileInfo f in assetFiles)
{
OVRPlatformToolSettings.AssetConfigs.Add(new AssetConfig(f.Name));
}
}
EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
}
// Display bordered asset configuration list
GUILayout.Space(3f);
Rect rect = GUILayoutUtility.GetRect(0, GetAssetConfigElementHeight() + (ASSET_CONFIG_BACKGROUND_PADDING * 2),
GUILayout.ExpandWidth(true));
rect.x += (EditorGUI.indentLevel * INDENT_SPACING + 5);
rect.width -= (EditorGUI.indentLevel * INDENT_SPACING + 10);
DrawAssetConfigList(rect);
DecrementIndent();
}
EditorGUI.indentLevel--;
}
if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
}
}
EditorGUILayout.EndScrollView();
GUILayout.Space(SINGLE_LINE_SPACING);
GUILayout.FlexibleSpace();
// Run OVR Lint Option
EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH;
GUIContent RunOvrLintLabel = new GUIContent("Run OVR Lint (Recommended) [?]: ",
"Run OVR Lint tool to ensure project is optimized for performance and meets Oculus packaging requirement for publishing.");
OVRPlatformToolSettings.RunOvrLint = MakeToggleBox(RunOvrLintLabel, OVRPlatformToolSettings.RunOvrLint);
// Add an Upload button
GUI.enabled = !activeProcess;
GUIContent btnTxt = new GUIContent("Upload");
var rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.ExpandWidth(false));
var btnYPos = rt.center.y;
rt.center = new Vector2(EditorGUIUtility.currentViewWidth / 2 - rt.width / 2 - buttonPadding, btnYPos);
if (GUI.Button(rt, btnTxt, GUI.skin.button))
{
OVRPlugin.SendEvent("oculus_platform_tool", "upload");
OVRPlatformTool.log = string.Empty;
OnUpload(OVRPlatformToolSettings.TargetPlatform);
}
// Add a cancel button
GUI.enabled = activeProcess;
btnTxt = new GUIContent("Cancel");
rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.ExpandWidth(false));
rt.center = new Vector2(EditorGUIUtility.currentViewWidth / 2 + rt.width / 2 + buttonPadding, btnYPos);
if (GUI.Button(rt, btnTxt, GUI.skin.button))
{
if (EditorUtility.DisplayDialog("Cancel Upload Process", "Are you sure you want to cancel the upload process?", "Yes", "No"))
{
if (ovrPlatUtilProcess != null)
{
ovrPlatUtilProcess.Kill();
OVRPlatformTool.log += "Upload process was canceled\n";
}
}
}
GUI.enabled = true;
GUILayout.FlexibleSpace();
GUILayout.Space(SINGLE_LINE_SPACING);
debugLogScroll = EditorGUILayout.BeginScrollView(debugLogScroll);
GUIStyle logBoxStyle = new GUIStyle();
logBoxStyle.margin.left = 5;
logBoxStyle.wordWrap = true;
logBoxStyle.normal.textColor = logBoxStyle.focused.textColor = EditorStyles.label.normal.textColor;
EditorGUILayout.SelectableLabel(OVRPlatformTool.log, logBoxStyle, GUILayout.Height(position.height - 30));
EditorGUILayout.EndScrollView();
// ODH Callout Section
GUILayout.BeginHorizontal(EditorStyles.helpBox);
var script = MonoScript.FromScriptableObject(this);
string assetPath = AssetDatabase.GetAssetPath(script);
string editorPath = Path.GetDirectoryName(assetPath);
string odhIconPath = Path.Combine(editorPath, "Textures\\odh_icon.png");
Texture ODHIcon = (Texture)EditorGUIUtility.Load(odhIconPath);
GUILayout.Box(ODHIcon, GUILayout.Width(60.0f), GUILayout.Height(60.0f));
GUILayout.BeginVertical();
EditorGUILayout.LabelField("<b>Oculus Developer Hub</b> is a desktop companion tool that can upload builds, manage apps and reduce friction in daily Quest development.",
odhCalloutStyle);
GUIContent ODHLabel = new GUIContent("Download Oculus Developer Hub");
#if UNITY_2021_1_OR_NEWER
if (EditorGUILayout.LinkButton(ODHLabel))
#else
if (GUILayout.Button(ODHLabel, GUILayout.ExpandWidth(false)))
#endif
{
#if UNITY_EDITOR_WIN
Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-win/?source=unity");
#elif UNITY_EDITOR_OSX
Application.OpenURL("https://developer.oculus.com/downloads/package/oculus-developer-hub-mac/?source=unity");
#endif
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
private void SetOVRProjectConfig(TargetPlatform targetPlatform)
{
#if UNITY_ANDROID
var targetDeviceTypes = new List<OVRProjectConfig.DeviceType>();
if (targetPlatform == TargetPlatform.Quest && !OVRDeviceSelector.isTargetDeviceQuestFamily)
{
targetDeviceTypes.Add(OVRProjectConfig.DeviceType.Quest);
targetDeviceTypes.Add(OVRProjectConfig.DeviceType.Quest2);
}
if (targetDeviceTypes.Count != 0)
{
OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
projectConfig.targetDeviceTypes = targetDeviceTypes;
OVRProjectConfig.CommitProjectConfig(projectConfig);
}
#endif
}
private void IncrementIndent()
{
EditorGUI.indentLevel++;
EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH - (EditorGUI.indentLevel * INDENT_SPACING);
}
private void DecrementIndent()
{
EditorGUI.indentLevel--;
EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH - (EditorGUI.indentLevel * INDENT_SPACING);
}
private void OnUpload(TargetPlatform targetPlatform)
{
OVRPlatformTool.log = string.Empty;
SetDirtyOnGUIChange();
var lintCount = 0;
if (OVRPlatformToolSettings.RunOvrLint)
{
lintCount = OVRLint.RunCheck();
}
if (lintCount != 0)
{
OVRPlatformTool.log += lintCount.ToString() + " lint suggestions are found. \n" +
"Please run Oculus\\Tools\\OVR Performance Lint Tool to review and fix lint errors. \n" +
"You can uncheck Run OVR Lint to bypass lint errors. \n";
OVRPlugin.SendEvent("oculus_platform_tool_lint", lintCount.ToString());
}
else
{
// Continue uploading process
ExecuteCommand(targetPlatform);
}
}
static void ExecuteCommand(TargetPlatform targetPlatform)
{
string dataPath = Application.dataPath;
// If we already have a copy of the platform util, check if it needs to be updated
if (!ranSelfUpdate && File.Exists(dataPath + "/Oculus/VR/Editor/Tools/ovr-platform-util.exe"))
{
ranSelfUpdate = true;
activeProcess = true;
var updateThread = new Thread(delegate () {
retryCount = 0;
CheckForUpdate(dataPath);
});
updateThread.Start();
}
string uploadCommand;
if (genUploadCommand(targetPlatform, out uploadCommand))
{
var thread = new Thread(delegate ()
{
// Wait for update process to finish before starting upload process
while (activeProcess)
{
Thread.Sleep(100);
}
retryCount = 0;
Command(targetPlatform, dataPath, uploadCommand);
});
thread.Start();
}
else
{
UnityEngine.Debug.LogError("Failed to generated upload command.");
}
}
private static string CheckForPlatformUtil(string dataPath)
{
string toolDataPath = dataPath + "/Oculus/VR/Editor/Tools";
if (!Directory.Exists(toolDataPath))
{
Directory.CreateDirectory(toolDataPath);
}
string platformUtil = toolDataPath + "/ovr-platform-util.exe";
if (!System.IO.File.Exists(platformUtil))
{
OVRPlugin.SendEvent("oculus_platform_tool", "provision_util");
EditorCoroutine downloadCoroutine = EditorCoroutine.Start(ProvisionPlatformUtil(platformUtil));
while (!downloadCoroutine.GetCompleted()) { }
}
return platformUtil;
}
private static void InitializePlatformUtilProcess(string path, string args)
{
ovrPlatUtilProcess = new Process();
var processInfo = new ProcessStartInfo(path, args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
ovrPlatUtilProcess.StartInfo = processInfo;
ovrPlatUtilProcess.EnableRaisingEvents = true;
}
static void CheckForUpdate(string dataPath)
{
string platformUtilPath = CheckForPlatformUtil(dataPath);
InitializePlatformUtilProcess(platformUtilPath, "self-update");
OVRPlatformTool.log += "Checking for update...\n";
ovrPlatUtilProcess.Exited += new EventHandler(
(s, e) =>
{
if (File.Exists(dataPath + ".ovr-platform-util.exe"))
{
OVRPlatformTool.log += "Cleaning up...\n";
while (File.Exists(dataPath + ".ovr-platform-util.exe")) { }
OVRPlatformTool.log += "Finished updating platform utility.\n";
}
activeProcess = false;
}
);
ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
(s, e) =>
{
if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b"))
{
OVRPlatformTool.log += e.Data + "\n";
}
}
);
try
{
ovrPlatUtilProcess.Start();
ovrPlatUtilProcess.BeginOutputReadLine();
}
catch
{
if (ThrowPlatformUtilStartupError(platformUtilPath))
{
CheckForUpdate(dataPath);
}
}
}
static void LoadRedistPackages(string dataPath)
{
// Check / Download the platform util and call list-redists on it
activeProcess = true;
string platformUtilPath = CheckForPlatformUtil(dataPath);
InitializePlatformUtilProcess(platformUtilPath, "list-redists");
OVRPlatformTool.log += "Loading redistributable packages...\n";
List<RedistPackage> redistPacks = new List<RedistPackage>();
ovrPlatUtilProcess.Exited += new EventHandler(
(s, e) =>
{
activeProcess = false;
}
);
ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
(s, e) =>
{
if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b") && !e.Data.Contains("ID"))
{
// Get the name / ID pair from the CLI and create a redist package instance
string[] terms = e.Data.Split('|');
if (terms.Length == 2)
{
RedistPackage redistPack = new RedistPackage(terms[1], terms[0]);
redistPacks.Add(redistPack);
}
}
}
);
try
{
ovrPlatUtilProcess.Start();
ovrPlatUtilProcess.BeginOutputReadLine();
ovrPlatUtilProcess.WaitForExit();
if (redistPacks.Count != OVRPlatformToolSettings.RiftRedistPackages.Count)
{
OVRPlatformTool.log += "Successfully updated redistributable packages.\n";
OVRPlatformToolSettings.RiftRedistPackages = redistPacks;
}
else
{
OVRPlatformTool.log += "Redistributable packages up to date.\n";
}
}
catch
{
if (ThrowPlatformUtilStartupError(platformUtilPath))
{
LoadRedistPackages(dataPath);
}
}
}
static void Command(TargetPlatform targetPlatform, string dataPath, string uploadCommand)
{
string platformUtilPath = CheckForPlatformUtil(dataPath);
activeProcess = true;
InitializePlatformUtilProcess(platformUtilPath, uploadCommand);
ovrPlatUtilProcess.Exited += new EventHandler(
(s, e) =>
{
activeProcess = false;
}
);
ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
(s, e) =>
{
if (e.Data != null && e.Data.Length != 0)
{
int index = e.Data.IndexOf("\u001b");
if (index >= 0)
{
OVRPlatformTool.log += e.Data.Substring(0, index) + "\n";
}
else
{
OVRPlatformTool.log += e.Data + "\n";
}
}
}
);
ovrPlatUtilProcess.ErrorDataReceived += new DataReceivedEventHandler(
(s, e) =>
{
OVRPlatformTool.log += e.Data + "\n";
}
);
try
{
ovrPlatUtilProcess.Start();
ovrPlatUtilProcess.BeginOutputReadLine();
ovrPlatUtilProcess.BeginErrorReadLine();
}
catch
{
if (ThrowPlatformUtilStartupError(platformUtilPath))
{
Command(targetPlatform, dataPath, uploadCommand);
}
}
}
private static bool genUploadCommand(TargetPlatform targetPlatform, out string command)
{
bool success = true;
command = "";
if (OVRPlatformToolSettings.UploadDebugSymbols && OVRPlatformToolSettings.UploadDebugSymbolsOnly)
{
return genDebugSymbolsCommand(out command);
}
else
{
switch (targetPlatform)
{
case TargetPlatform.Rift:
command = "upload-rift-build";
break;
case TargetPlatform.Quest:
command = "upload-quest-build";
break;
default:
OVRPlatformTool.log += "ERROR: Invalid target platform selected";
success = false;
break;
}
}
// Add App ID
ValidateTextField(IDFieldValidator, OVRPlatformToolSettings.AppID, "App ID", ref success);
command += " --app-id \"" + OVRPlatformToolSettings.AppID + "\"";
// Add App Token
ValidateTextField(GenericFieldValidator, appToken, "App Token", ref success);
command += " --app-secret \"" + appToken + "\"";
// Add Platform specific fields
if (targetPlatform == TargetPlatform.Rift)
{
// Add Rift Build Directory
ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.RiftBuildDirectory, "Rift Build Directory", ref success);
command += " --build-dir \"" + OVRPlatformToolSettings.RiftBuildDirectory + "\"";
// Add Rift Launch File
ValidateTextField(FileValidator, OVRPlatformToolSettings.RiftLaunchFile, "Rift Launch File Path", ref success);
command += " --launch-file \"" + OVRPlatformToolSettings.RiftLaunchFile + "\"";
// Add Rift Build Version
ValidateTextField(GenericFieldValidator, OVRPlatformToolSettings.RiftBuildVersion, "Build Version", ref success);
command += " --version \"" + OVRPlatformToolSettings.RiftBuildVersion + "\"";
// Add Rift Launch Parameters
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.RiftLaunchParams))
{
ValidateTextField(LaunchParameterValidator, OVRPlatformToolSettings.RiftLaunchParams, "Launch Parameters", ref success);
command += " --launch_params \"" + OVRPlatformToolSettings.RiftLaunchParams + "\"";
}
// Add 2D Launch File
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.Rift2DLaunchFile))
{
ValidateTextField(FileValidator, OVRPlatformToolSettings.Rift2DLaunchFile, "2D Launch File", ref success);
command += " --launch_file_2d \"" + OVRPlatformToolSettings.Rift2DLaunchFile + "\"";
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.Rift2DLaunchParams))
{
ValidateTextField(LaunchParameterValidator, OVRPlatformToolSettings.Rift2DLaunchParams, "2D Launch Parameters", ref success);
command += " --launch_params_2d \"" + OVRPlatformToolSettings.Rift2DLaunchParams + "\"";
}
}
// Add Firewall Exception
if (OVRPlatformToolSettings.RiftFirewallException)
{
command += " --firewall_exceptions true";
}
// Add Redistributable Packages
List<string> redistCommandIds = new List<string>();
for (int i = 0; i < OVRPlatformToolSettings.RiftRedistPackages.Count; i++)
{
if (OVRPlatformToolSettings.RiftRedistPackages[i].include)
{
redistCommandIds.Add(OVRPlatformToolSettings.RiftRedistPackages[i].id);
}
}
if (redistCommandIds.Count > 0)
{
command += " --redistributables \"" + string.Join(",", redistCommandIds.ToArray()) + "\"";
}
// Add Gamepad Emulation
if (OVRPlatformToolSettings.RiftGamepadEmulation > GamepadType.OFF &&
OVRPlatformToolSettings.RiftGamepadEmulation <= GamepadType.LEFT_D_PAD)
{
command += " --gamepad-emulation ";
switch (OVRPlatformToolSettings.RiftGamepadEmulation)
{
case GamepadType.TWINSTICK: command += "TWINSTICK"; break;
case GamepadType.RIGHT_D_PAD: command += "RIGHT_D_PAD"; break;
case GamepadType.LEFT_D_PAD: command += "LEFT_D_PAD"; break;
default: command += "OFF"; break;
}
}
// Add Rift Language Pack Directory
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.LanguagePackDirectory))
{
ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.LanguagePackDirectory, "Language Pack Directory", ref success);
command += " --language_packs_dir \"" + OVRPlatformToolSettings.LanguagePackDirectory + "\"";
}
}
else
{
// Add APK Build Path
ValidateTextField(FileValidator, OVRPlatformToolSettings.ApkBuildPath, "APK Build Path", ref success);
command += " --apk \"" + OVRPlatformToolSettings.ApkBuildPath + "\"";
// Add OBB File Path
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.ObbFilePath))
{
ValidateTextField(FileValidator, OVRPlatformToolSettings.ObbFilePath, "OBB File Path", ref success);
command += " --obb \"" + OVRPlatformToolSettings.ObbFilePath + "\"";
}
// Add Debug Symbols Path
if (OVRPlatformToolSettings.UploadDebugSymbols)
{
ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.DebugSymbolsDirectory, "Debug Symbols Directory", ref success);
command += " --debug-symbols-dir \"" + OVRPlatformToolSettings.DebugSymbolsDirectory + "\"";
command += " --debug-symbols-pattern \"*.sym.so\"";
}
if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
{
// Quest specific commands
}
}
// Add Assets Directory
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.AssetsDirectory))
{
ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.AssetsDirectory, "Assets Directory", ref success);
command += " --assets-dir \"" + OVRPlatformToolSettings.AssetsDirectory + "\"";
// Add Asset Configurations
if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
{
List<string> assetConfigs = new List<string>();
for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
{
List<string> configParameters = new List<string>();
AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
if (config.required)
{
configParameters.Add("\\\"required\\\":true");
}
if (config.type > AssetConfig.AssetType.DEFAULT)
{
string typeCommand = "\\\"type\\\":";
switch (config.type)
{
case AssetConfig.AssetType.LANGUAGE_PACK:
configParameters.Add(typeCommand + "\\\"LANGUAGE_PACK\\\"");
break;
case AssetConfig.AssetType.STORE:
configParameters.Add(typeCommand + "\\\"STORE\\\"");
break;
default:
configParameters.Add(typeCommand + "\\\"DEFAULT\\\"");
break;
}
}
if (!string.IsNullOrEmpty(config.sku))
{
configParameters.Add("\\\"sku\\\":\\\"" + config.sku + "\\\"");
}
if (configParameters.Count > 0)
{
string configString = "\\\"" + config.name + "\\\":{" + string.Join(",", configParameters.ToArray()) + "}";
assetConfigs.Add(configString);
}
}
if (assetConfigs.Count > 0)
{
command += " --asset_files_config {" + string.Join(",", assetConfigs.ToArray()) + "}";
}
}
}
// Add Release Channel
ValidateTextField(GenericFieldValidator, OVRPlatformToolSettings.ReleaseChannel, "Release Channel", ref success);
command += " --channel \"" + OVRPlatformToolSettings.ReleaseChannel + "\"";
// Add Notes
if (!string.IsNullOrEmpty(OVRPlatformToolSettings.ReleaseNote))
{
string sanatizedReleaseNote = OVRPlatformToolSettings.ReleaseNote;
sanatizedReleaseNote = sanatizedReleaseNote.Replace("\"", "\"\"");
command += " --notes \"" + sanatizedReleaseNote + "\"";
}
return success;
}
private static bool genDebugSymbolsCommand(out string command)
{
bool success = true;
command = "upload-debug-symbols";
// Add Build ID
ValidateTextField(IDFieldValidator, OVRPlatformToolSettings.BuildID, "Build ID", ref success);
command += " --parent \"" + OVRPlatformToolSettings.BuildID + "\"";
// Add App ID
ValidateTextField(IDFieldValidator, OVRPlatformToolSettings.AppID, "App ID", ref success);
command += " --app-id \"" + OVRPlatformToolSettings.AppID + "\"";
// Add App Token
ValidateTextField(GenericFieldValidator, appToken, "App Token", ref success);
command += " --app-secret \"" + appToken + "\"";
ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.DebugSymbolsDirectory, "Debug Symbols Directory", ref success);
command += " --debug-symbols-dir \"" + OVRPlatformToolSettings.DebugSymbolsDirectory + "\"";
command += " --debug-symbols-pattern \"*.sym.so\"";
return success;
}
// Private delegate for text field validation functions
private delegate TSuccess FieldValidatorDelegate<in TText, TError, out TSuccess>(TText text, ref TError error);
// Validate the text using a given field validator function. An error message will be printed if validation fails. Success will ONLY be modified to false if validation fails.
static void ValidateTextField(FieldValidatorDelegate<string, string, bool> fieldValidator, string fieldText, string fieldName, ref bool success)
{
string error = "";
if (!fieldValidator(fieldText, ref error))
{
OVRPlatformTool.log += "ERROR: Please verify that the " + fieldName + " is correct. ";
OVRPlatformTool.log += string.IsNullOrEmpty(error) ? "\n" : error + "\n";
success = false;
}
}
// Checks if the text is null or empty
static bool GenericFieldValidator(string fieldText, ref string error)
{
if (string.IsNullOrEmpty(fieldText))
{
error = "The field is empty.";
return false;
}
return true;
}
// Checks if the App ID contains only numbers
static bool IDFieldValidator(string fieldText, ref string error)
{
if (string.IsNullOrEmpty(fieldText))
{
error = "The field is empty.";
return false;
}
else if (!Regex.IsMatch(fieldText, "^[0-9]+$"))
{
error = "The field contains invalid characters.";
return false;
}
return true;
}
// Check that the directory exists
static bool DirectoryValidator(string path, ref string error)
{
if (!Directory.Exists(path))
{
error = "The directory does not exist.";
return false;
}
return true;
}
// Check that the file exists
static bool FileValidator(string path, ref string error)
{
if (!File.Exists(path))
{
error = "The file does not exist.";
return false;
}
return true;
}
// Check if the launch parameter string contains illegal characters
static bool LaunchParameterValidator(string fieldText, ref string error)
{
if (fieldText.Contains("\""))
{
error = "The field contains illegal characters.";
return false;
}
return true;
}
void OnInspectorUpdate()
{
Repaint();
}
private static bool ThrowPlatformUtilStartupError(string utilPath)
{
if (retryCount < MAX_DOWNLOAD_RETRY_COUNT)
{
retryCount++;
OVRPlatformTool.log += "There was a problem starting Oculus Platform Util. Restarting provision process...\n";
File.Delete(utilPath);
return true;
}
else
{
OVRPlatformTool.log += "OVR Platform Tool had a problem with downloading a valid executable after several trys. Please reopen the tool to try again.\n";
return false;
}
}
private string MakeTextBox(GUIContent label, string variable)
{
string result = string.Empty;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
result = EditorGUILayout.TextField(variable);
EditorGUILayout.EndHorizontal();
return result;
}
private string MakePasswordBox(GUIContent label, string variable)
{
string result = string.Empty;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
result = EditorGUILayout.PasswordField(variable);
EditorGUILayout.EndHorizontal();
return result;
}
private bool MakeToggleBox(GUIContent label, bool variable)
{
bool result = false;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
result = EditorGUILayout.Toggle(variable);
EditorGUILayout.EndHorizontal();
return result;
}
private Enum MakeEnumPopup(GUIContent label, Enum selected)
{
Enum result;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
result = EditorGUILayout.EnumPopup(selected);
EditorGUILayout.EndHorizontal();
return result;
}
private string MakeFileDirectoryField(GUIContent label, string variable, string title, bool isFile = false, string extension = "")
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
Rect rect = GUILayoutUtility.GetRect(0, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(true));
EditorGUI.SelectableLabel(rect, variable);
// Create X button if there is a valid path in the field
string result = variable;
if (!string.IsNullOrEmpty(variable))
{
Color defaultColor = GUI.backgroundColor;
GUI.backgroundColor = new Color(.9f, 0.5f, 0.5f);
rect = GUILayoutUtility.GetRect(SINGLE_LINE_SPACING, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(false));
if (GUI.Button(rect, "X"))
{
result = string.Empty;
}
GUI.backgroundColor = defaultColor;
}
// Create the Choose button to initiate the file explorer
rect = GUILayoutUtility.GetRect(75f, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(false));
if (GUI.Button(rect, "Choose ..."))
{
string newPath = string.Empty;
string path = string.IsNullOrEmpty(variable) ? Application.dataPath : variable;
if (isFile)
{
newPath = EditorUtility.OpenFilePanel(title, path, extension);
}
else
{
newPath = EditorUtility.OpenFolderPanel(title, path, string.Empty);
}
if (newPath.Length > 0)
{
result = newPath;
}
}
GUILayout.Space(5f);
EditorGUILayout.EndHorizontal();
// If the path has changed, deselect the selectable field so that it can update.
if (result != variable)
{
GUIUtility.hotControl = 0;
GUIUtility.keyboardControl = 0;
}
return result;
}
private static void SetDirtyOnGUIChange()
{
if (GUI.changed)
{
EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
GUI.changed = false;
}
}
private static IEnumerator ProvisionPlatformUtil(string dataPath)
{
UnityEngine.Debug.Log("Started Provisioning Oculus Platform Util");
var webRequest = new UnityWebRequest(urlPlatformUtil, UnityWebRequest.kHttpVerbGET);
string path = dataPath;
webRequest.downloadHandler = new DownloadHandlerFile(path);
// WWW request timeout in seconds
webRequest.timeout = 60;
UnityWebRequestAsyncOperation webOp = webRequest.SendWebRequest();
while (!webOp.isDone) { }
#if UNITY_2020_1_OR_NEWER
if (webRequest.result == UnityWebRequest.Result.ConnectionError ||
webRequest.result == UnityWebRequest.Result.ProtocolError)
#else
if (webRequest.isNetworkError || webRequest.isHttpError)
#endif
{
var networkErrorMsg = "Failed to provision Oculus Platform Util\n";
UnityEngine.Debug.LogError(networkErrorMsg);
OVRPlatformTool.log += networkErrorMsg;
}
else
{
OVRPlatformTool.log += "Completed Provisioning Oculus Platform Util\n";
}
SetDirtyOnGUIChange();
yield return webOp;
}
private static void DrawAssetConfigList(Rect rect)
{
DrawAssetConfigHeader(rect);
DrawAssetConfigBackground(rect);
DrawAssetConfigElement(rect);
}
private static void DrawAssetConfigElement(Rect rect)
{
Rect elementRect = new Rect(rect.x, rect.y + SINGLE_LINE_SPACING + ASSET_CONFIG_BACKGROUND_PADDING / 2,
rect.width, SINGLE_LINE_SPACING);
if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
{
for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
{
AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
GUIContent fieldLabel;
config.SetFoldoutState(EditorGUI.Foldout(elementRect, config.GetFoldoutState(), config.name, boldFoldoutStyle));
if (config.GetFoldoutState())
{
Rect attributeRect = new Rect(elementRect.x + INDENT_SPACING, elementRect.y + SINGLE_LINE_SPACING,
elementRect.width - INDENT_SPACING - 3f, SINGLE_LINE_SPACING);
// Extra asset config params are disabled for now until CLI supports them
#if !DISABLE_EXTRA_ASSET_CONFIG
fieldLabel = new GUIContent("Required Asset [?]", "Whether or not this asset file is required for the app to run.");
config.required = EditorGUI.Toggle(attributeRect, fieldLabel, config.required);
attributeRect.y += SINGLE_LINE_SPACING;
fieldLabel = new GUIContent("Asset Type [?]", "The asset file type.");
config.type = (AssetConfig.AssetType)EditorGUI.EnumPopup(attributeRect, fieldLabel, config.type);
attributeRect.y += SINGLE_LINE_SPACING;
#endif
fieldLabel = new GUIContent("Asset SKU [?]", "The Oculus store SKU for this asset file.");
config.sku = EditorGUI.TextField(attributeRect, fieldLabel, config.sku);
elementRect.y = attributeRect.y;
}
elementRect.y += SINGLE_LINE_SPACING;
}
}
else
{
EditorGUI.LabelField(elementRect, "No asset files found. Choose a valid assets directory.");
}
}
private static float GetAssetConfigElementHeight()
{
float totalHeight = 0f;
if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
{
for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
{
AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
#if !DISABLE_EXTRA_ASSET_CONFIG
totalHeight += config.GetFoldoutState() ? SINGLE_LINE_SPACING * 4 : SINGLE_LINE_SPACING;
#else
totalHeight += config.GetFoldoutState() ? SINGLE_LINE_SPACING * 2 : SINGLE_LINE_SPACING;
#endif
}
}
else
{
totalHeight += SINGLE_LINE_SPACING;
}
return totalHeight + ASSET_CONFIG_BACKGROUND_PADDING;
}
private static void DrawAssetConfigHeader(Rect rect)
{
Rect headerRect = new Rect(rect.x, rect.y, rect.width, SINGLE_LINE_SPACING);
EditorGUI.DrawRect(headerRect, EditorGUIUtility.isProSkin ? new Color(0.37f, 0.37f, 0.37f) : new Color(0.55f, 0.55f, 0.55f));
EditorGUI.LabelField(rect, "Asset File Configuration");
}
private static void DrawAssetConfigBackground(Rect rect)
{
Rect backgroundRect = new Rect(rect.x, rect.y + SINGLE_LINE_SPACING, rect.width, GetAssetConfigElementHeight());
EditorGUI.DrawRect(backgroundRect, EditorGUIUtility.isProSkin ? new Color(0.3f, 0.3f, 0.3f) : new Color(0.63f, 0.63f, 0.63f));
}
class GUIHelper
{
public delegate void Worker();
static void InOut(Worker begin, Worker body, Worker end)
{
try
{
begin();
body();
}
finally
{
end();
}
}
public static void HInset(int pixels, Worker worker)
{
InOut(
() => {
GUILayout.BeginHorizontal();
GUILayout.Space(pixels);
GUILayout.BeginVertical();
},
worker,
() => {
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
);
}
public delegate T ControlWorker<T>();
public static T MakeControlWithLabel<T>(GUIContent label, ControlWorker<T> worker)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label);
var result = worker();
EditorGUILayout.EndHorizontal();
return result;
}
}
public class EditorCoroutine
{
public static EditorCoroutine Start(IEnumerator routine)
{
EditorCoroutine coroutine = new EditorCoroutine(routine);
coroutine.Start();
return coroutine;
}
readonly IEnumerator routine;
bool completed;
EditorCoroutine(IEnumerator _routine)
{
routine = _routine;
completed = false;
}
void Start()
{
EditorApplication.update += Update;
}
public void Stop()
{
EditorApplication.update -= Update;
completed = true;
}
public bool GetCompleted()
{
return completed;
}
void Update()
{
if (!routine.MoveNext())
{
Stop();
}
}
}
}
}