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;
}
}
}