using System.IO; using System.Linq; using AppleHills.Editor.Utilities; using Bootstrap; using UnityEditor; using UnityEditor.AddressableAssets.Settings; using UnityEditorInternal; using UnityEngine; using UnityEngine.AddressableAssets; namespace Editor.Bootstrap { /// /// Helper methods for CustomBoot configuration /// public class CustomBootSettingsUtil { /// /// Path to the ProjectSettings file /// private const string PROJECT_SETTINGS_PATH = "ProjectSettings/CustomBoot.asset"; /// /// Path to the runtime custom boot settings file /// private const string RUNTIME_CUSTOM_BOOT_SETTINGS_PATH = "Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset"; /// /// Path to the editor custom boot settings file /// private const string EDITOR_CUSTOM_BOOT_SETTINGS_PATH = "Assets/Data/Bootstrap/Editor/CustomBootSettings_Editor.asset"; /// /// Determine whether the settings asset file is available /// /// internal static bool IsSettingsAvailable() { return File.Exists(PROJECT_SETTINGS_PATH); } /// /// Retrieve the settings object if it exists, otherwise create and return it. /// /// internal static CustomBootProjectSettings GetOrCreateSettings() { CustomBootProjectSettings projectSettings; //Check whether the settings file already exists if (IsSettingsAvailable()) { //If it exists, load it projectSettings = InternalEditorUtility.LoadSerializedFileAndForget(PROJECT_SETTINGS_PATH).First() as CustomBootProjectSettings; } else { //If it doesn't exist, create a new ScriptableObject projectSettings = ScriptableObject.CreateInstance(); //Configure the settings file CreateBootSettingsAssets(out var runtimeEntry, out var editorEntry); projectSettings.RuntimeSettings = new AssetReference(runtimeEntry.guid); projectSettings.EditorSettings = new AssetReference(editorEntry.guid); //And save it! InternalEditorUtility.SaveToSerializedFileAndForget(new Object[] { projectSettings }, PROJECT_SETTINGS_PATH, true); } //Finally, return our settings object return projectSettings; } /// /// Create the Runtime and Editor CustomBootSettings assets. /// /// /// private static void CreateBootSettingsAssets(out AddressableAssetEntry runtimeEntry, out AddressableAssetEntry editorEntry) { //Create two assets representing our boot configurations var runtimeSettings = GetOrCreateBootSettingsAsset(RUNTIME_CUSTOM_BOOT_SETTINGS_PATH, out var runtimeCreated); var editorSettings = GetOrCreateBootSettingsAsset(EDITOR_CUSTOM_BOOT_SETTINGS_PATH, out var editorCreated); //Save the AssetDatabase state if either asset is new if (runtimeCreated || editorCreated) { AssetDatabase.SaveAssets(); } //Configure the Addressables system with the new assets. AddSettingsToAddressables(runtimeSettings, editorSettings, out runtimeEntry, out editorEntry); } /// /// Load, or create, a CustomBootSettings asset at the given path /// /// /// /// private static CustomBootSettings GetOrCreateBootSettingsAsset(string path, out bool wasCreated) { var settings = AssetDatabase.LoadAssetAtPath(path); if (!settings) { //Make sure full path is created var dirPath = Path.GetDirectoryName(path); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } settings = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(settings, path); wasCreated = true; } else { wasCreated = false; } return settings; } /// /// Add the CustomBootSettings asset to the relevant Addressables groups. /// /// /// /// /// private static void AddSettingsToAddressables(CustomBootSettings runtimeSettings, CustomBootSettings editorSettings, out AddressableAssetEntry runtimeEntry, out AddressableAssetEntry editorEntry) { InitialiseAddressableGroups(out var runtimeGroup, out var editorGroup); runtimeEntry = CreateCustomBootSettingsEntry(runtimeSettings, runtimeGroup, $"{nameof(CustomBootSettings)}_Runtime"); editorEntry = CreateCustomBootSettingsEntry(editorSettings, editorGroup, $"{nameof(CustomBootSettings)}_Editor"); AssetDatabase.SaveAssets(); } /// /// Create an Addressables entry for the given CustomBootSettings object, and add it to the given group. /// /// /// /// /// private static AddressableAssetEntry CreateCustomBootSettingsEntry(CustomBootSettings bootSettings, AddressableAssetGroup group, string key) { string assetPath = AssetDatabase.GetAssetPath(bootSettings); var settings = AddressablesUtility.GetAddressableSettings(false); if (settings == null || string.IsNullOrEmpty(assetPath) || group == null) { return null; } var guid = AssetDatabase.AssetPathToGUID(assetPath); var entry = settings.CreateOrMoveEntry(guid, group); entry.address = key; settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entry, true); return entry; } /// /// Ensure the Runtime and Editor Addressables groups exist /// /// /// private static void InitialiseAddressableGroups(out AddressableAssetGroup runtimeGroup, out AddressableAssetGroup editorGroup) { runtimeGroup = GetOrCreateGroup($"{nameof(CustomBoot)}_Runtime", true); editorGroup = GetOrCreateGroup($"{nameof(CustomBoot)}_Editor", false); } /// /// Retrieve or create an Addressables group. /// /// /// /// private static AddressableAssetGroup GetOrCreateGroup(string name, bool includeInBuild) { return AddressablesUtility.GetOrCreateGroup(name, includeInBuild); } /// /// Retrieve the serialised representation of the settings object /// /// internal static SerializedObject GetSerializedSettings() { return new SerializedObject(GetOrCreateSettings()); } } }