/* * 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.Reflection; using UnityEngine; using UnityEditor; using PackageInfo = UnityEditor.PackageManager.PackageInfo; using Debug = UnityEngine.Debug; using System.Diagnostics; using System.Collections.Generic; namespace Oculus.Interaction { [InitializeOnLoad] public class PluginUpdater { private static bool _isRestarting = false; private static string _keyBase = "Oculus_Interaction_PluginUpdater"; private static string _keyDontAsk = _keyBase + "_DontAsk"; private static string _keyIsInstalling = _keyBase + "_IsRestarting"; private static BuildTarget[] buildTargets = { BuildTarget.Android, BuildTarget.StandaloneWindows, BuildTarget.StandaloneWindows64 }; private static string _baseDllName = "InteractionSdk"; static PluginUpdater() { EditorApplication.delayCall += HandleDelayCall; } [MenuItem("Meta/Interaction/Update Interaction SDK Plugin")] public static void UpdatePlugin() { PerformUpdate(verbose: true); } private static string GetProjectPath() { string path = Application.dataPath; string token = "/Assets"; if (path.EndsWith(token)) { path = path.Substring(0, path.Length - token.Length); } return path; } public static void HandleDelayCall() { if (!EditorApplication.isPlaying && !_isRestarting) { // first off, check to see if there's NO current dll for the build targets, and if so just copy the new one over right now since there won't be a chance of write blocking foreach (BuildTarget t in buildTargets) { if (IsNewLibraryPresent(t) && !IsCurrentLibraryPresent(t)) { MoveNewLibary(t); } } // only do the moving when not running and not called from command line in batch mode if (!Application.isBatchMode) { // has an install been started and Unity just got restarted? // If so, start moving the new libraries into the names where the old ones were located if (PlayerPrefs.GetInt(_keyIsInstalling, 0) > 0) { foreach (BuildTarget t in buildTargets) { if (IsNewLibraryPresent(t)) { MoveNewLibary(t); } } // mark as done for next time Unity starts PlayerPrefs.SetInt(_keyIsInstalling, 0); } else if (PlayerPrefs.GetInt(_keyDontAsk, 0) == 0) { // if user hasn't asked us to stop bugging them, go on and ask 'em PerformUpdate(verbose: false); } } } } private static string GetTargetFolderName(BuildTarget target) { switch (target) { case BuildTarget.Android: return "Android"; case BuildTarget.StandaloneOSX: return "OSX"; case BuildTarget.StandaloneWindows: return "Win32"; case BuildTarget.StandaloneWindows64: return "Win64"; default: throw new ArgumentException("Attempted GetTargetFolderName() for unsupported BuildTarget: " + target); } } private static string GetTargetDllSuffix(BuildTarget target) { switch (target) { case BuildTarget.Android: return ".aar"; case BuildTarget.StandaloneOSX: return ".bundle"; case BuildTarget.StandaloneWindows: return ".dll"; case BuildTarget.StandaloneWindows64: return ".dll"; default: throw new ArgumentException("Attempted GetTargetDllSuffix() for unsupported BuildTarget: " + target); } } private static string[] FindAllSdkDlls(BuildTarget target) { char[] slashes = new char[] { '/', '\\' }; List dllPaths = new List(); string[] dlls = AssetDatabase.FindAssets(_baseDllName); string targetPath = GetTargetFolderName(target); foreach (string dll in dlls) { string path = AssetDatabase.GUIDToAssetPath(dll); string suffix = GetTargetDllSuffix(target); if (path.Contains(_baseDllName + suffix) && !path.Contains(_baseDllName + suffix + ".disabled")) { // see if the path contains the build target name (e.g. Win64) we're looking for string[] subPaths = path.Split(slashes); foreach (string subpath in subPaths) { if (subpath == targetPath) { dllPaths.Add(path); break; } } } } return dllPaths.ToArray(); } private static void PerformUpdate(bool verbose) { int newLibsFound = 0; foreach (BuildTarget t in buildTargets) { if (IsNewLibraryPresent(t)) { newLibsFound++; } } if (newLibsFound > 0) { // the ordering of the dialog responses is to keep consistency with the OVRPluginUpdater dialog responses int response = EditorUtility.DisplayDialogComplex("Update Interaction SDK libraries?", "New versions of Interaction SDK libraries were found, it is recommended you update them now.", "Ok", "No, Don't ask again", "Not right now"); switch (response) { case 0: // yes if (MoveAllNewLibraries() > 0) { PlayerPrefs.SetInt(_keyIsInstalling, 1); response = EditorUtility.DisplayDialogComplex("Restart Unity?", "Unity needs to be restarted in order for the Interaction SDK installation to complete", "Ok", "Not right now", "Cancel"); if (response == 0) RestartUnity(); } else { PlayerPrefs.SetInt(_keyIsInstalling, 0); EditorUtility.DisplayDialog("Library update error", "There was an issue updating the new Interaction SDK libraries", "Ok"); return; } break; case 1: // dont ask again PlayerPrefs.SetInt(_keyDontAsk, 1); break; case 2: // no break; } } else { if (verbose) { EditorUtility.DisplayDialog("Interaction SDK Plugin Updater", "No new libraries were found to update", "Ok"); } } } private static int MoveAllNewLibraries() { int found = 0; foreach (BuildTarget t in buildTargets) { if (IsNewLibraryPresent(t)) { found++; if (IsCurrentLibraryPresent(t)) { MoveCurrentLibrary(t); } } } return found; } private static string GetCurrentLibraryAssetPath(BuildTarget target, bool newVersion) { string[] dlls = FindAllSdkDlls(target); foreach (var dllName in dlls) { string suffix = GetTargetDllSuffix(target); if (dllName.Contains(suffix)) { bool isNew = dllName.Contains(suffix + ".new"); if (newVersion == isNew) return dllName; } } return null; } private static bool IsCurrentLibraryPresent(BuildTarget target) { return GetCurrentLibraryAssetPath(target, false) != null; } private static bool IsNewLibraryPresent(BuildTarget target) { return GetCurrentLibraryAssetPath(target, true) != null; } private static bool MoveCurrentLibrary(BuildTarget target) { string srcPath = GetCurrentLibraryAssetPath(target, false); bool success = MoveAsset(srcPath, srcPath + ".disabled", true); return success; } private static bool MoveNewLibary(BuildTarget target) { string src = GetCurrentLibraryAssetPath(target, true); string dest = src; if (dest.EndsWith(".new")) { dest = dest.Substring(0, dest.Length - 4); } bool success = MoveAsset(src, dest, false); if (success) { PluginImporter pi = PluginImporter.GetAtPath(dest) as PluginImporter; pi.SetCompatibleWithEditor(false); pi.SetCompatibleWithAnyPlatform(false); pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, false); pi.SetCompatibleWithPlatform(BuildTarget.Android, false); pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false); pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false); pi.SetCompatibleWithPlatform(target, true); pi.SetCompatibleWithEditor(true); switch (target) { case BuildTarget.StandaloneOSX: pi.SetCompatibleWithEditor(true); pi.SetEditorData("CPU", "AnyCPU"); pi.SetEditorData("OS", "OSX"); pi.SetPlatformData("Editor", "CPU", "AnyCPU"); pi.SetPlatformData("Editor", "OS", "OSX"); break; case BuildTarget.StandaloneWindows: pi.SetEditorData("CPU", "X86"); pi.SetEditorData("OS", "Windows"); pi.SetPlatformData("Editor", "CPU", "X86"); pi.SetPlatformData("Editor", "OS", "Windows"); break; case BuildTarget.Android: break; case BuildTarget.StandaloneWindows64: pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, true); pi.SetCompatibleWithEditor(true); pi.SetEditorData("CPU", "X86_64"); pi.SetEditorData("OS", "Windows"); pi.SetPlatformData("Editor", "CPU", "X86_64"); pi.SetPlatformData("Editor", "OS", "Windows"); break; } ReimportAsset(dest); } else { Debug.LogErrorFormat("ISDK PluginUpdater: Error copying {0} to {1}", src, dest); } return success; } private static void ReimportAsset(string path) { AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); } private static bool MoveAsset(string srcPath, string destPath, bool reImport) { string fullDest = GetProjectPath() + "/" + destPath; if (File.Exists(fullDest)) { File.Delete(fullDest); File.Delete(fullDest + ".meta"); } string errMsg = AssetDatabase.MoveAsset(srcPath, destPath); if (errMsg.Length > 0) { UnityEngine.Debug.LogError(errMsg); return false; } if (reImport) { ReimportAsset(destPath); } return true; } private static void RestartUnity() { _isRestarting = true; EditorApplication.OpenProject(GetProjectPath()); } } }