// /*=============================================================================== // Copyright (C) 2020 PhantomsXR Ltd. All Rights Reserved. // // This file is part of the AR-MOD SDK. // // The AR-MOD SDK cannot be copied, distributed, or made available to // third-parties for commercial purposes without written permission of PhantomsXR Ltd. // // Contact info@phantomsxr.com for licensing requests. // ===============================================================================*/ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Phantom.XRMOD.ActionNotification.Runtime; using Phantom.XRMOD.Core.Runtime; using Phantom.XRMOD.XRMODPackageTools.Editor.Filter; using Phantom.XRMOD.XRMODPackageTools.Runtime; using UnityEditor; using UnityEditor.Build.Pipeline; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.UIElements; using Phantom.XRMOD.XRMODUtilites.Runtime; using Object = UnityEngine.Object; namespace Phantom.XRMOD.XRMODPackageTools.Editor { public class BuildView : VisualElement { private static ProjectDetailCacheData projectDetailCacheData; private ProjectModel projectModel; private const string CONST_BUILD_PLATFORM_NAME = "Build Platform"; private const string CONST_BUILD_GROUP_NAME = "Platform Group"; private const string CONST_COMPRESSION_TYPE_NAME = "Compression Type"; private const string CONST_ERROR_TITLE = "ERROR"; public new class UxmlFactory : UxmlFactory { } public BuildView() { style.width = resolvedStyle.width; if (InitData()) { InitElements(); } } private void InitElements() { BuildPlatformEnumList(); MakeHorizontalButtonList(); ActionNotificationCenter.DefaultCenter.AddObserver(_data => { UpdateData(); }, nameof(EditingProjectCommand)); } private void BuildPlatformEnumList() { var tmp_BuildPlatformEnumField = new EnumField(CONST_BUILD_PLATFORM_NAME, Utility.GetBuildTargetViaSDKDeviceType(projectDetailCacheData.Configures.SDKDeviceType)) {name = "buildtarget"}; var tmp_PlatformGroupEnumField = new EnumField(CONST_BUILD_GROUP_NAME, Utility.GetBuildTargetGroupViaSDKDeviceType(projectDetailCacheData.Configures.SDKDeviceType)) {name = "buildtargetgroup"}; var tmp_CompressionTypeEnumField = new EnumField(CONST_COMPRESSION_TYPE_NAME, projectDetailCacheData.BuildSettingData.BuildCompression) {name = "compressiontype"}; Add(tmp_BuildPlatformEnumField); Add(tmp_PlatformGroupEnumField); Add(tmp_CompressionTypeEnumField); tmp_BuildPlatformEnumField.RegisterCallback>(_evt => { if (Enum.TryParse(_evt.newValue.ToString(), out BuildTarget tmp_Result)) { projectDetailCacheData.BuildSettingData.BuildTarget = tmp_Result; } }); tmp_PlatformGroupEnumField.RegisterCallback>(_evt => { if (Enum.TryParse(_evt.newValue.ToString(), out BuildTargetGroup tmp_Result)) { projectDetailCacheData.BuildSettingData.BuildTargetGroup = tmp_Result; } }); tmp_CompressionTypeEnumField.RegisterCallback>(_evt => { if (Enum.TryParse(_evt.newValue.ToString(), out BuildCompressionType tmp_Result)) { projectDetailCacheData.BuildSettingData.BuildCompression = tmp_Result; } }); } private void MakeHorizontalButtonList() { var tmp_BuildButton = new Button(async () => { StartBuildExperience(); }) {text = "Build Experience", name = "builbutton"}; tmp_BuildButton.AddToClassList("build-button"); var tmp_DropdownButton = new VisualElement(); var tmp_DropdownButtonImg = new VisualElement(); tmp_DropdownButtonImg.AddToClassList("dropdown-button-image"); tmp_DropdownButton.Add(tmp_DropdownButtonImg); tmp_DropdownButton.AddManipulator(new Clickable(OpenDropdownMenu)); tmp_DropdownButton.AddToClassList("unity-button"); tmp_DropdownButton.AddToClassList("dropdown-button"); var tmp_HorizontalVisualElement = new VisualElement(); tmp_HorizontalVisualElement.style.flexDirection = new StyleEnum(FlexDirection.RowReverse); tmp_HorizontalVisualElement.Add(tmp_DropdownButton); tmp_HorizontalVisualElement.Add(tmp_BuildButton); tmp_HorizontalVisualElement.AddToClassList("horizontal-button"); Add(tmp_HorizontalVisualElement); } private void OpenDropdownMenu() { GenericMenu tmp_Menu = new GenericMenu(); tmp_Menu.AddItem(new GUIContent("Build (MonoBinder Convert)"), false, () => { StartBuildExperience(true); }); tmp_Menu.AddSeparator(""); tmp_Menu.AddItem(new GUIContent("Show in file browser"), false, async () => { var tmp_Path = Path.Combine(Application.dataPath.Replace("Assets", ""), $"ServerData/{RuntimePlatformHelper.GetPlatformName().ToLower()}" + $"/{projectModel.DisplayName.ToLower()}/{projectModel.DisplayName.ToLower()}.json"); if (File.Exists(tmp_Path)) EditorUtility.RevealInFinder(tmp_Path); else EditorUtility.DisplayDialog(CONST_ERROR_TITLE, "You haven't built an AR-Experience", "Ok"); }); tmp_Menu.AddItem(new GUIContent("Refresh"), false, RefreshAppDomain); tmp_Menu.ShowAsContext(); } private async void StartBuildExperience(bool _autoMonoBinderConvert = false) { if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return; if (projectDetailCacheData.Configures) EditorUtility.SetDirty(projectDetailCacheData.Configures); if (projectDetailCacheData.BuildSettingData) EditorUtility.SetDirty(projectDetailCacheData.BuildSettingData); AssetDatabase.SaveAssets(); projectModel.IsBuilding = true; projectDetailCacheData.BuildSettingData.AutoConvertOptions = _autoMonoBinderConvert; BuildDll(); await Task.Delay(500); ExecuteBuildAssetBundle(); projectModel.IsBuilding = false; } private bool InitData() { var tmp_EditingProjectData = PackageToolsEditor.ALL_PROJECT_CACHE.GetEditingProjectData(); if (!tmp_EditingProjectData) return false; projectModel = tmp_EditingProjectData.Project; projectDetailCacheData = tmp_EditingProjectData.DetailCacheData; return true; } private void UpdateData() { InitData(); //Reset data var tmp_BuildTarget = this.Q("buildtarget"); if (tmp_BuildTarget != null) tmp_BuildTarget.value = projectDetailCacheData.BuildSettingData.BuildTarget; var tmp_BuildGroup = this.Q("buildtargetgroup"); if (tmp_BuildGroup != null) tmp_BuildGroup.value = projectDetailCacheData.BuildSettingData.BuildTargetGroup; var tmp_BuildCompression = this.Q("compressiontype"); if (tmp_BuildCompression != null) tmp_BuildCompression.value = projectDetailCacheData.BuildSettingData.BuildCompression; } private static async void RefreshAppDomain() { try { await ActionNotificationCenter.DefaultCenter.PostNotificationAsync("AutoInjectPostProcessor", new BaseNotificationData()); string tmp_ProjectAutoGenerationPath = Path.Combine(projectDetailCacheData.GetProjectPath(), "AutomaticGenerated"); if (!Directory.Exists(tmp_ProjectAutoGenerationPath)) Directory.CreateDirectory(tmp_ProjectAutoGenerationPath); if (string.IsNullOrEmpty(projectDetailCacheData.Configures.DomainName)) return; var tmp_SourceFilePath = Path.Combine(Utility.GetScriptAssembliesFolder, new DirectoryInfo(projectDetailCacheData.GetProjectPath()).Name); Utility.CopyFileToProject($"{tmp_SourceFilePath}.runtime.dll", tmp_ProjectAutoGenerationPath, "bytes"); Utility.CopyFileToProject($"{tmp_SourceFilePath}.runtime.pdb", tmp_ProjectAutoGenerationPath, "bytes"); } catch (Exception e) { Debug.LogError(e); throw; // TODO handle exception } } private async void ExecuteBuildAssetBundle() { var tmp_ProjectName = new DirectoryInfo(projectDetailCacheData.GetProjectPath()).Name; List tmp_AddressableName = new List(); List tmp_BundlePath = new List(); string[] dontCopyFilter = new[] { $"{tmp_ProjectName}.runtime.dll.bytes".ToLower(), $"{tmp_ProjectName}.runtime.pdb.bytes".ToLower(), "ARProperty.json", "LocalizationTable.csv", "RuntimeAssetReferenceDatabase.asset" }; string tmp_ProjectAutoGenerationPath = Path.Combine(projectDetailCacheData.GetProjectPath(), "AutomaticGenerated"); if (!Directory.Exists(tmp_ProjectAutoGenerationPath)) Directory.CreateDirectory(tmp_ProjectAutoGenerationPath); // ReSharper disable once InconsistentNaming var tmp_ARPropertyJsonFilePath = Path.Combine(tmp_ProjectAutoGenerationPath, "ARProperty.json"); await File.WriteAllTextAsync(tmp_ARPropertyJsonFilePath, JsonUtility.ToJson(projectDetailCacheData.Configures)); // if (!string.IsNullOrEmpty(projectDetailCacheData.Configures.DomainName)) if (projectDetailCacheData.Configures.ProgrammableType == ProgrammableType.CSharp) { var tmp_SourceFilePath = Path.Combine(Utility.GetScriptAssembliesFolder, $"Runtime/InjectWorkSpace/{tmp_ProjectName}"); string tmp_DllAssetPath = Utility.CopyFileToProject($"{tmp_SourceFilePath}.Runtime.dll", tmp_ProjectAutoGenerationPath, "bytes"); string tmp_PdbAssetPath = Utility.CopyFileToProject($"{tmp_SourceFilePath}.Runtime.pdb", tmp_ProjectAutoGenerationPath, "bytes"); //Not allowed to be empty if (string.IsNullOrEmpty(tmp_DllAssetPath) || string.IsNullOrEmpty(tmp_PdbAssetPath)) return; var tmp_RenameDllFileToLower = Path.GetFileName(tmp_DllAssetPath).Replace(".bytes", "").ToLower(); var tmp_RenamePdbFileToLower = Path.GetFileName(tmp_PdbAssetPath).Replace(".bytes", "").ToLower(); tmp_AddressableName.Add(tmp_RenameDllFileToLower); tmp_AddressableName.Add(tmp_RenamePdbFileToLower); tmp_BundlePath.Add(tmp_DllAssetPath); tmp_BundlePath.Add(tmp_PdbAssetPath); } // Auto append needed data tmp_AddressableName.Add(Path.GetFileNameWithoutExtension(tmp_ARPropertyJsonFilePath)); tmp_BundlePath.Add(Utility.ShortenPath(tmp_ARPropertyJsonFilePath)); // Auto append localizationTable var tmp_LocalizationTablePath = Path.Combine(projectDetailCacheData.GetProjectPath(), "Artwork/LocalizationTable.csv"); var tmp_LocalizationTableNameWithoutExtension = Path.GetFileNameWithoutExtension(tmp_LocalizationTablePath); if (!tmp_AddressableName.Contains(tmp_LocalizationTableNameWithoutExtension)) { tmp_AddressableName.Add(tmp_LocalizationTableNameWithoutExtension); tmp_BundlePath.Add(Utility.ShortenPath(tmp_LocalizationTablePath)); } // Auto append RuntimeAssetReferenceDatabase var tmp_RuntimeReferenceAssetDbPath = Path.Combine(projectDetailCacheData.GetProjectPath(), $"Cache/{nameof(RuntimeAssetReferenceDatabase)}.asset"); if (!File.Exists(tmp_RuntimeReferenceAssetDbPath)) { var tmp_RuntimeAssetReferenceDatabase = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(tmp_RuntimeAssetReferenceDatabase, Utility.ShortenPath(tmp_RuntimeReferenceAssetDbPath)); } var tmp_RRANameWithoutExtension = Path.GetFileNameWithoutExtension(tmp_RuntimeReferenceAssetDbPath); if (!tmp_AddressableName.Contains(tmp_RRANameWithoutExtension)) { tmp_AddressableName.Add(Path.GetFileNameWithoutExtension(tmp_RuntimeReferenceAssetDbPath)); tmp_BundlePath.Add(Utility.ShortenPath(tmp_RuntimeReferenceAssetDbPath)); } var tmp_GenericesFilter = new GenericesAssetFilter(); var tmp_GenericesData = tmp_GenericesFilter.Filter(projectDetailCacheData.Contents); foreach (var tmp_ContentData in tmp_GenericesData) { if (string.IsNullOrEmpty(tmp_ContentData.AssetPathInUnity)) { Debug.LogError( $"Missing the asset with {tmp_ContentData.DisplayName} at {tmp_ContentData.AssetPathInUnity}"); continue; } if (string.IsNullOrEmpty(tmp_ContentData.DisplayName)) continue; tmp_AddressableName.Add(Path.GetFileName(tmp_ContentData.DisplayName)); tmp_BundlePath.Add(tmp_ContentData.AssetPathInUnity); } var tmp_SavedBundlePath = Path.Combine(Application.dataPath.Replace("Assets", "ServerData"), RuntimePlatformHelper.GetPlatformName().ToLower()); tmp_SavedBundlePath = Path.Combine(tmp_SavedBundlePath, projectModel.DisplayName.ToLower()); if (!Directory.Exists(tmp_SavedBundlePath)) Directory.CreateDirectory(tmp_SavedBundlePath); BuildCompression tmp_BuildCompression = BuildCompression.LZ4; switch (projectDetailCacheData.BuildSettingData.BuildCompression) { case BuildCompressionType.Uncompressed: tmp_BuildCompression = BuildCompression.Uncompressed; break; case BuildCompressionType.LZ4: tmp_BuildCompression = BuildCompression.LZ4; break; case BuildCompressionType.LZMA: tmp_BuildCompression = BuildCompression.LZMA; break; case BuildCompressionType.UncompressedRuntime: tmp_BuildCompression = BuildCompression.UncompressedRuntime; break; case BuildCompressionType.LZ4Runtime: tmp_BuildCompression = BuildCompression.LZ4Runtime; break; } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Create a temp folder to save the temp assets var tmp_CacheFolderPath = projectDetailCacheData.GetProjectPath(); var tmp_TempAssetFolderPath = Path.Combine(tmp_CacheFolderPath, "AutomaticGenerated/TempAssets"); if (!Directory.Exists(tmp_TempAssetFolderPath)) { Directory.CreateDirectory(tmp_TempAssetFolderPath); } else { // Delete all temp assets AssetDatabase.DeleteAsset(Utility.ShortenPath(tmp_TempAssetFolderPath)); Directory.CreateDirectory(tmp_TempAssetFolderPath); } // Copy all assets to temp folder List tmp_TempAssetsPath = new List(); foreach (var tmp_AssetPath in tmp_BundlePath) { var tmp_FileName = Path.GetFileName(tmp_AssetPath); var tmp_DestFilePath = Utility.ShortenPath(Path.Combine(tmp_TempAssetFolderPath, tmp_FileName)); // 不拷贝这些文件 if (dontCopyFilter.Contains(tmp_FileName)) { tmp_TempAssetsPath.Add(tmp_AssetPath); continue; } AssetDatabase.CopyAsset(tmp_AssetPath, tmp_DestFilePath); tmp_TempAssetsPath.Add(tmp_DestFilePath); } // Check the prefabs // Remove components when Enable state is dissabled // Add all temp assets to build list // Convert XRMODBehaviour to MonoBinder var tmp_AllPrefabPaths = tmp_TempAssetsPath.Where(_path => _path.EndsWith("prefab")).ToArray(); BaseNotificationData tmp_NotificationData = new BaseNotificationData(); foreach (var tmp_PrefabPath in tmp_AllPrefabPaths) { var tmp_PrefabGo = AssetDatabase.LoadAssetAtPath(tmp_PrefabPath); tmp_NotificationData.ObjectData = tmp_PrefabGo; if (projectDetailCacheData.BuildSettingData.AutoConvertOptions) await ActionNotificationCenter.DefaultCenter.PostNotificationAsync("ConvertToMonoBinderBatch", tmp_NotificationData); } await Task.Delay(100); foreach (string tmp_PrefabPath in tmp_AllPrefabPaths) { var tmp_PrefabGo = AssetDatabase.LoadAssetAtPath(tmp_PrefabPath); var tmp_AllScripts = tmp_PrefabGo.GetComponentsInChildren(true); foreach (XRMODBehaviour tmp_Script in tmp_AllScripts) { if (tmp_Script == null || tmp_Script.enabled) continue; Object.DestroyImmediate(tmp_Script, true); EditorUtility.SetDirty(tmp_PrefabGo); } } var tmp_AllScenePaths = tmp_TempAssetsPath.Where(_path => _path.EndsWith("unity")).ToArray(); foreach (var tmp_ScenePath in tmp_AllScenePaths) { var tmp_OpenScene = EditorSceneManager.OpenScene(tmp_ScenePath, OpenSceneMode.Additive); var tmp_AllXRMODBehaviour = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); var tmp_DisabledComponent = tmp_AllXRMODBehaviour.Where(_component => !_component.enabled).ToArray(); foreach (XRMODBehaviour tmp_Script in tmp_DisabledComponent) { if (tmp_Script == null || tmp_Script.enabled) continue; var tmp_CurrentGo = tmp_Script.gameObject; Object.DestroyImmediate(tmp_Script, true); EditorUtility.SetDirty(tmp_CurrentGo); } EditorSceneManager.SaveScene(tmp_OpenScene); EditorSceneManager.CloseScene(tmp_OpenScene, true); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); var tmp_StateCode = BundlePacker.ScriptableBuildPipeline(projectModel.DisplayName.ToLower(), tmp_AddressableName, tmp_TempAssetsPath, projectDetailCacheData.BuildSettingData.BuildTarget, projectDetailCacheData.BuildSettingData.BuildTargetGroup, tmp_BuildCompression, tmp_SavedBundlePath ); if (tmp_StateCode == ReturnCode.UnsavedChanges) { EditorUtility.DisplayDialog("ERROR", "Your Scene was changed,But you not saved changes", "OK"); } AssetDatabase.Refresh(); if (Event.current is {rawType: EventType.Repaint}) GUIUtility.ExitGUI(); } private async void BuildDll() { var tmp_Group = BuildPipeline.GetBuildTargetGroup(projectDetailCacheData.BuildSettingData.BuildTarget); UnityEditor.Build.Player.ScriptCompilationSettings tmp_ScriptCompilationSettings = new UnityEditor.Build.Player.ScriptCompilationSettings(); tmp_ScriptCompilationSettings.group = tmp_Group; tmp_ScriptCompilationSettings.target = projectDetailCacheData.BuildSettingData.BuildTarget; var tmp_SourceFilePath = Path.Combine(Utility.GetScriptAssembliesFolder, "Runtime"); Directory.CreateDirectory(tmp_SourceFilePath); UnityEditor.Build.Player.ScriptCompilationResult tmp_ScriptCompilationResult = UnityEditor.Build.Player.PlayerBuildInterface.CompilePlayerScripts(tmp_ScriptCompilationSettings, tmp_SourceFilePath); await ActionNotificationCenter.DefaultCenter.PostNotificationAsync("AutoInjectPostProcessor", new BaseNotificationData()); #if UNITY_2022 UnityEditor.EditorUtility.ClearProgressBar(); #endif } } }