using System; using System.Collections.Generic; using System.IO; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEngine; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement.Util; using UnityEngine.Serialization; namespace UnityEditor.AddressableAssets.Build.DataBuilders { /// /// Base class for build script assets /// public class BuildScriptBase : ScriptableObject, IDataBuilder { /// /// The type of instance provider to create for the Addressables system. /// [FormerlySerializedAs("m_InstanceProviderType")] [SerializedTypeRestrictionAttribute(type = typeof(IInstanceProvider))] public SerializedType instanceProviderType = new SerializedType() { Value = typeof(InstanceProvider) }; /// /// The type of scene provider to create for the addressables system. /// [FormerlySerializedAs("m_SceneProviderType")] [SerializedTypeRestrictionAttribute(type = typeof(ISceneProvider))] public SerializedType sceneProviderType = new SerializedType() { Value = typeof(SceneProvider) }; /// /// The descriptive name used in the UI. /// public virtual string Name { get { return "Undefined"; } } /// /// Build the specified data with the provided builderInput. This is the public entry point. /// Child class overrides should use /// /// The type of data to build. /// The builderInput object used in the build. /// The build data result. public TResult BuildData(AddressablesDataBuilderInput builderInput) where TResult : IDataBuilderResult { if (!CanBuildData()) { var message = "Data builder " + Name + " cannot build requested type: " + typeof(TResult); Debug.LogError(message); return AddressableAssetBuildResult.CreateResult(null, 0, message); } // Append the file registry to the results var result = BuildDataImplementation(builderInput); if (result != null) result.FileRegistry = builderInput.Registry; return result; } /// /// The implementation of . That is the public entry point, /// this is the home for child class overrides. /// /// The builderInput object used in the build /// The type of data to build /// The build data result protected virtual TResult BuildDataImplementation(AddressablesDataBuilderInput builderInput) where TResult : IDataBuilderResult { return default(TResult); } /// /// Loops over each group, after doing some data checking. /// /// The Addressables builderInput object to base the group processing on /// An error string if there were any problems processing the groups protected virtual string ProcessAllGroups(AddressableAssetsBuildContext aaContext) { if (aaContext == null || aaContext.settings == null || aaContext.settings.groups == null) { return "No groups found to process in build script " + Name; } //intentionally for not foreach so groups can be added mid-loop. for(int index = 0; index < aaContext.settings.groups.Count; index++) { AddressableAssetGroup assetGroup = aaContext.settings.groups[index]; var errorString = ProcessGroup(assetGroup, aaContext); if(!string.IsNullOrEmpty(errorString)) return errorString; } return string.Empty; } /// /// Build processing of an individual group. /// /// The group to process /// The Addressables builderInput object to base the group processing on /// An error string if there were any problems processing the groups protected virtual string ProcessGroup(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext) { return string.Empty; } /// /// Used to determine if this builder is capable of building a specific type of data. /// /// The type of data needed to be built. /// True if this builder can build this data. public virtual bool CanBuildData() where T : IDataBuilderResult { return false; } /// /// Utility method for creating locations from player data. /// /// The group to extract the locations from. /// The list of created locations to fill in. /// True if any legacy locations were created. This is used by the build scripts to determine if a legacy provider is needed. protected bool CreateLocationsForPlayerData(PlayerDataGroupSchema playerDataSchema, AddressableAssetGroup assetGroup, List locations) { bool needsLegacyProvider = false; if (playerDataSchema != null && (playerDataSchema.IncludeBuildSettingsScenes || playerDataSchema.IncludeResourcesFolders)) { var entries = new List(); assetGroup.GatherAllAssets(entries, true, true); foreach (var a in entries) { if (!playerDataSchema.IncludeBuildSettingsScenes && a.IsInSceneList) continue; if (!playerDataSchema.IncludeResourcesFolders && a.IsInResources) continue; a.CreateCatalogEntries(locations, false, a.IsScene ? "" : typeof(LegacyResourcesProvider).FullName, null, null); if (!a.IsScene) needsLegacyProvider = true; } } return needsLegacyProvider; } /// /// Utility method for deleting files. /// /// The file path to delete. protected static void DeleteFile(string path) { try { if (File.Exists(path)) File.Delete(path); } catch (Exception ex) { Debug.LogException(ex); } } /// /// Utility method to write a file. The directory will be created if it does not exist. /// /// The path of the file to write. /// The content of the file. /// The file registry used to track all produced artifacts. /// True if the file was written. protected static bool WriteFile(string path, string content, FileRegistry registry) { try { registry.AddFile(path); var dir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); File.WriteAllText(path, content); return true; } catch (Exception ex) { Debug.LogException(ex); registry.RemoveFile(path); return false; } } /// /// Used to clean up any cached data created by this builder. /// public virtual void ClearCachedData() { } /// /// Checks to see if the data is built for the given builder. /// public virtual bool IsDataBuilt() { return false; } } }