initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
|
@ -0,0 +1,167 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.Build.Pipeline.Interfaces;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
using UnityEngine.AddressableAssets.ResourceLocators;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Build.DataBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for any Addressables specific context objects to be used in the Scriptable Build Pipeline context store
|
||||
/// </summary>
|
||||
public interface IAddressableAssetsBuildContext : IContextObject
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple context object for passing data through SBP, between different sections of Addressables code.
|
||||
/// </summary>
|
||||
public class AddressableAssetsBuildContext : IAddressableAssetsBuildContext
|
||||
{
|
||||
private AddressableAssetSettings m_Settings;
|
||||
|
||||
/// <summary>
|
||||
/// The settings object to use.
|
||||
/// </summary>
|
||||
[Obsolete("Use Settings property instead.")]
|
||||
public AddressableAssetSettings settings;
|
||||
|
||||
/// <summary>
|
||||
/// The settings object to use.
|
||||
/// </summary>
|
||||
public AddressableAssetSettings Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Settings == null && !string.IsNullOrEmpty(m_SettingsAssetPath))
|
||||
m_Settings = AssetDatabase.LoadAssetAtPath<AddressableAssetSettings>(m_SettingsAssetPath);
|
||||
return m_Settings;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_Settings = value;
|
||||
string guid;
|
||||
if (m_Settings != null && AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_Settings, out guid, out long localId))
|
||||
m_SettingsAssetPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
else
|
||||
m_SettingsAssetPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_SettingsAssetPath;
|
||||
|
||||
/// <summary>
|
||||
/// The time the build started
|
||||
/// </summary>
|
||||
public DateTime buildStartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The current runtime data being built.
|
||||
/// </summary>
|
||||
public ResourceManagerRuntimeData runtimeData;
|
||||
|
||||
/// <summary>
|
||||
/// The list of catalog locations.
|
||||
/// </summary>
|
||||
public List<ContentCatalogDataEntry> locations;
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of bundles to asset groups.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> bundleToAssetGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of asset group to bundles.
|
||||
/// </summary>
|
||||
public Dictionary<AddressableAssetGroup, List<string>> assetGroupToBundles;
|
||||
|
||||
/// <summary>
|
||||
/// Set of provider types needed in this build.
|
||||
/// </summary>
|
||||
public HashSet<Type> providerTypes;
|
||||
|
||||
/// <summary>
|
||||
/// The list of all AddressableAssetEntry objects.
|
||||
/// </summary>
|
||||
public List<AddressableAssetEntry> assetEntries;
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of AssetBundle to the direct dependencies.
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>> bundleToImmediateBundleDependencies;
|
||||
|
||||
/// <summary>
|
||||
/// A mapping of AssetBundle to the full dependency tree, flattened into a single list.
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>> bundleToExpandedBundleDependencies;
|
||||
|
||||
/// <summary>
|
||||
/// A mapping of Asset GUID's to resulting ContentCatalogDataEntry entries.
|
||||
/// </summary>
|
||||
internal Dictionary<GUID, List<ContentCatalogDataEntry>> GuidToCatalogLocation = null;
|
||||
|
||||
private Dictionary<string, List<ContentCatalogDataEntry>> m_PrimaryKeyToDependers = null;
|
||||
|
||||
internal Dictionary<string, List<ContentCatalogDataEntry>> PrimaryKeyToDependerLocations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_PrimaryKeyToDependers != null)
|
||||
return m_PrimaryKeyToDependers;
|
||||
if (locations == null || locations.Count == 0)
|
||||
{
|
||||
Debug.LogError("Attempting to get Entries dependent on key, but currently no locations");
|
||||
return new Dictionary<string, List<ContentCatalogDataEntry>>(0);
|
||||
}
|
||||
|
||||
m_PrimaryKeyToDependers = new Dictionary<string, List<ContentCatalogDataEntry>>(locations.Count);
|
||||
foreach (ContentCatalogDataEntry location in locations)
|
||||
{
|
||||
for (int i = 0; i < location.Dependencies.Count; ++i)
|
||||
{
|
||||
string dependencyKey = location.Dependencies[i] as string;
|
||||
if (string.IsNullOrEmpty(dependencyKey))
|
||||
continue;
|
||||
|
||||
if (!m_PrimaryKeyToDependers.TryGetValue(dependencyKey, out var dependers))
|
||||
{
|
||||
dependers = new List<ContentCatalogDataEntry>();
|
||||
m_PrimaryKeyToDependers.Add(dependencyKey, dependers);
|
||||
}
|
||||
|
||||
dependers.Add(location);
|
||||
}
|
||||
}
|
||||
|
||||
return m_PrimaryKeyToDependers;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, ContentCatalogDataEntry> m_PrimaryKeyToLocation = null;
|
||||
|
||||
internal Dictionary<string, ContentCatalogDataEntry> PrimaryKeyToLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_PrimaryKeyToLocation != null)
|
||||
return m_PrimaryKeyToLocation;
|
||||
if (locations == null || locations.Count == 0)
|
||||
{
|
||||
Debug.LogError("Attempting to get Primary key to entries dependent on key, but currently no locations");
|
||||
return new Dictionary<string, ContentCatalogDataEntry>();
|
||||
}
|
||||
|
||||
m_PrimaryKeyToLocation = new Dictionary<string, ContentCatalogDataEntry>();
|
||||
foreach (var loc in locations)
|
||||
{
|
||||
if (loc != null && loc.Keys[0] != null && loc.Keys[0] is string && !m_PrimaryKeyToLocation.ContainsKey((string)loc.Keys[0]))
|
||||
m_PrimaryKeyToLocation[(string)loc.Keys[0]] = loc;
|
||||
}
|
||||
|
||||
return m_PrimaryKeyToLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bdd672a655042ae4fa4c3f8c8d8853a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEditor.Build.Pipeline;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Build.Content;
|
||||
using BuildCompression = UnityEngine.BuildCompression;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Build.DataBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom bundle parameter container that provides custom compression settings per bundle.
|
||||
/// </summary>
|
||||
public class AddressableAssetsBundleBuildParameters : BundleBuildParameters
|
||||
{
|
||||
Dictionary<string, string> m_bundleToAssetGroup;
|
||||
AddressableAssetSettings m_settings;
|
||||
|
||||
/// <summary>
|
||||
/// Create a AddressableAssetsBundleBuildParameters with data needed to determine the correct compression per bundle.
|
||||
/// </summary>
|
||||
/// <param name="aaSettings">The AddressableAssetSettings object to use for retrieving groups.</param>
|
||||
/// <param name="bundleToAssetGroup">Mapping of bundle identifier to guid of asset groups.</param>
|
||||
/// <param name="target">The build target. This is used by the BundleBuildParameters base class.</param>
|
||||
/// <param name="group">The build target group. This is used by the BundleBuildParameters base class.</param>
|
||||
/// <param name="outputFolder">The path for the output folder. This is used by the BundleBuildParameters base class.</param>
|
||||
public AddressableAssetsBundleBuildParameters(AddressableAssetSettings aaSettings, Dictionary<string, string> bundleToAssetGroup, BuildTarget target, BuildTargetGroup group,
|
||||
string outputFolder) : base(target, group, outputFolder)
|
||||
{
|
||||
UseCache = true;
|
||||
ContiguousBundles = aaSettings.ContiguousBundles;
|
||||
#if NONRECURSIVE_DEPENDENCY_DATA
|
||||
NonRecursiveDependencies = aaSettings.NonRecursiveBuilding;
|
||||
#endif
|
||||
DisableVisibleSubAssetRepresentations = aaSettings.DisableVisibleSubAssetRepresentations;
|
||||
|
||||
m_settings = aaSettings;
|
||||
m_bundleToAssetGroup = bundleToAssetGroup;
|
||||
|
||||
//If default group has BundledAssetGroupSchema use the compression there otherwise check if the target is webgl or not and try set the compression accordingly
|
||||
if (m_settings.DefaultGroup.HasSchema<BundledAssetGroupSchema>())
|
||||
BundleCompression = ConverBundleCompressiontToBuildCompression(m_settings.DefaultGroup.GetSchema<BundledAssetGroupSchema>().Compression);
|
||||
else
|
||||
BundleCompression = target == BuildTarget.WebGL ? BuildCompression.LZ4Runtime : BuildCompression.LZMA;
|
||||
|
||||
if (aaSettings.StripUnityVersionFromBundleBuild)
|
||||
ContentBuildFlags |= ContentBuildFlags.StripUnityVersion;
|
||||
}
|
||||
|
||||
private BuildCompression ConverBundleCompressiontToBuildCompression(
|
||||
BundledAssetGroupSchema.BundleCompressionMode compressionMode)
|
||||
{
|
||||
BuildCompression compresion = BuildCompression.LZMA;
|
||||
switch (compressionMode)
|
||||
{
|
||||
case BundledAssetGroupSchema.BundleCompressionMode.LZMA:
|
||||
break;
|
||||
case BundledAssetGroupSchema.BundleCompressionMode.LZ4:
|
||||
compresion = BuildCompression.LZ4;
|
||||
break;
|
||||
case BundledAssetGroupSchema.BundleCompressionMode.Uncompressed:
|
||||
compresion = BuildCompression.Uncompressed;
|
||||
break;
|
||||
}
|
||||
|
||||
return compresion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the compressions settings for the specified asset bundle.
|
||||
/// </summary>
|
||||
/// <param name="identifier">The identifier of the asset bundle.</param>
|
||||
/// <returns>The compression setting for the asset group. If the group is not found, the default compression is used.</returns>
|
||||
public override BuildCompression GetCompressionForIdentifier(string identifier)
|
||||
{
|
||||
string groupGuid;
|
||||
if (m_bundleToAssetGroup.TryGetValue(identifier, out groupGuid))
|
||||
{
|
||||
var group = m_settings.FindGroup(g => g != null && g.Guid == groupGuid);
|
||||
if (group != null)
|
||||
{
|
||||
var abSchema = group.GetSchema<BundledAssetGroupSchema>();
|
||||
if (abSchema != null)
|
||||
return abSchema.GetBuildCompressionForBundle(identifier);
|
||||
else
|
||||
Debug.LogWarningFormat("Bundle group {0} does not have BundledAssetGroupSchema.", group.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("Unable to find group with guid {0}", groupGuid);
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetCompressionForIdentifier(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e4e9f0f7eaff4094990720ec2b944faf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEditor.Build.Pipeline.Interfaces;
|
||||
using UnityEditor.Build.Pipeline.Utilities;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
using UnityEngine.AddressableAssets.ResourceLocators;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Build.DataBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for build script assets
|
||||
/// </summary>
|
||||
public class BuildScriptBase : ScriptableObject, IDataBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of instance provider to create for the Addressables system.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("m_InstanceProviderType")]
|
||||
[SerializedTypeRestrictionAttribute(type = typeof(IInstanceProvider))]
|
||||
public SerializedType instanceProviderType = new SerializedType() {Value = typeof(InstanceProvider)};
|
||||
|
||||
/// <summary>
|
||||
/// The type of scene provider to create for the addressables system.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("m_SceneProviderType")]
|
||||
[SerializedTypeRestrictionAttribute(type = typeof(ISceneProvider))]
|
||||
public SerializedType sceneProviderType = new SerializedType() {Value = typeof(SceneProvider)};
|
||||
|
||||
/// <summary>
|
||||
/// Stores the logged information of all the build tasks.
|
||||
/// </summary>
|
||||
public IBuildLogger Log
|
||||
{
|
||||
get { return m_Log; }
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
internal IBuildLogger m_Log;
|
||||
|
||||
/// <summary>
|
||||
/// The descriptive name used in the UI.
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get { return "Undefined"; }
|
||||
}
|
||||
|
||||
internal static void WriteBuildLog(BuildLog log, string directory)
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
PackageManager.PackageInfo info = PackageManager.PackageInfo.FindForAssembly(typeof(BuildScriptBase).Assembly);
|
||||
log.AddMetaData(info.name, info.version);
|
||||
File.WriteAllText(Path.Combine(directory, "AddressablesBuildTEP.json"), log.FormatForTraceEventProfiler());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the specified data with the provided builderInput. This is the public entry point.
|
||||
/// Child class overrides should use <see cref="BuildDataImplementation{TResult}"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">The type of data to build.</typeparam>
|
||||
/// <param name="builderInput">The builderInput object used in the build.</param>
|
||||
/// <returns>The build data result.</returns>
|
||||
public TResult BuildData<TResult>(AddressablesDataBuilderInput builderInput) where TResult : IDataBuilderResult
|
||||
{
|
||||
if (!CanBuildData<TResult>())
|
||||
{
|
||||
var message = "Data builder " + Name + " cannot build requested type: " + typeof(TResult);
|
||||
Debug.LogError(message);
|
||||
return AddressableAssetBuildResult.CreateResult<TResult>(null, 0, message);
|
||||
}
|
||||
|
||||
AddressableAnalytics.BuildType buildType = AddressableAnalytics.DetermineBuildType();
|
||||
m_Log = (builderInput.Logger != null) ? builderInput.Logger : new BuildLog();
|
||||
|
||||
AddressablesRuntimeProperties.ClearCachedPropertyValues();
|
||||
|
||||
TResult result = default;
|
||||
// Append the file registry to the results
|
||||
using (m_Log.ScopedStep(LogLevel.Info, $"Building {this.Name}"))
|
||||
{
|
||||
try
|
||||
{
|
||||
result = BuildDataImplementation<TResult>(builderInput);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string errMessage;
|
||||
if (e.Message == "path")
|
||||
errMessage = "Invalid path detected during build. Check for unmatched brackets in your active profile's variables.";
|
||||
else
|
||||
errMessage = e.Message;
|
||||
|
||||
Debug.LogError(errMessage);
|
||||
return AddressableAssetBuildResult.CreateResult<TResult>(null, 0, errMessage);
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
result.FileRegistry = builderInput.Registry;
|
||||
}
|
||||
|
||||
if (builderInput.Logger == null && m_Log != null)
|
||||
WriteBuildLog((BuildLog)m_Log, Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath);
|
||||
|
||||
if (result is AddressableAssetBuildResult)
|
||||
{
|
||||
AddressableAnalytics.ReportBuildEvent(builderInput, result as AddressableAssetBuildResult, buildType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The implementation of <see cref="BuildData{TResult}"/>. That is the public entry point,
|
||||
/// this is the home for child class overrides.
|
||||
/// </summary>
|
||||
/// <param name="builderInput">The builderInput object used in the build</param>
|
||||
/// <typeparam name="TResult">The type of data to build</typeparam>
|
||||
/// <returns>The build data result</returns>
|
||||
protected virtual TResult BuildDataImplementation<TResult>(AddressablesDataBuilderInput builderInput) where TResult : IDataBuilderResult
|
||||
{
|
||||
return default(TResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops over each group, after doing some data checking.
|
||||
/// </summary>
|
||||
/// <param name="aaContext">The Addressables builderInput object to base the group processing on</param>
|
||||
/// <returns>An error string if there were any problems processing the groups</returns>
|
||||
protected virtual string ProcessAllGroups(AddressableAssetsBuildContext aaContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
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];
|
||||
if (assetGroup == null)
|
||||
continue;
|
||||
|
||||
if (assetGroup.Schemas.Find((x) => x.GetType() == typeof(PlayerDataGroupSchema)) &&
|
||||
assetGroup.Schemas.Find((x) => x.GetType() == typeof(BundledAssetGroupSchema)))
|
||||
{
|
||||
return $"Addressable group {assetGroup.Name} cannot have both a {typeof(PlayerDataGroupSchema).Name} and a {typeof(BundledAssetGroupSchema).Name}";
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar($"Processing Addressable Group", assetGroup.Name, (float)index / aaContext.Settings.groups.Count);
|
||||
var errorString = ProcessGroup(assetGroup, aaContext);
|
||||
if (!string.IsNullOrEmpty(errorString))
|
||||
{
|
||||
return errorString;
|
||||
}
|
||||
}
|
||||
} finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build processing of an individual group.
|
||||
/// </summary>
|
||||
/// <param name="assetGroup">The group to process</param>
|
||||
/// <param name="aaContext">The Addressables builderInput object to base the group processing on</param>
|
||||
/// <returns>An error string if there were any problems processing the groups</returns>
|
||||
protected virtual string ProcessGroup(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to determine if this builder is capable of building a specific type of data.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data needed to be built.</typeparam>
|
||||
/// <returns>True if this builder can build this data.</returns>
|
||||
public virtual bool CanBuildData<T>() where T : IDataBuilderResult
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method for creating locations from player data.
|
||||
/// </summary>
|
||||
/// <param name="playerDataSchema">The schema for the group.</param>
|
||||
/// <param name="assetGroup">The group to extract the locations from.</param>
|
||||
/// <param name="locations">The list of created locations to fill in.</param>
|
||||
/// <param name="providerTypes">Any unknown provider types are added to this set in order to ensure they are not stripped.</param>
|
||||
/// <returns>True if any legacy locations were created. This is used by the build scripts to determine if a legacy provider is needed.</returns>
|
||||
protected bool CreateLocationsForPlayerData(PlayerDataGroupSchema playerDataSchema, AddressableAssetGroup assetGroup, List<ContentCatalogDataEntry> locations, HashSet<Type> providerTypes)
|
||||
{
|
||||
bool needsLegacyProvider = false;
|
||||
if (playerDataSchema != null && (playerDataSchema.IncludeBuildSettingsScenes || playerDataSchema.IncludeResourcesFolders))
|
||||
{
|
||||
var entries = new List<AddressableAssetEntry>();
|
||||
assetGroup.GatherAllAssets(entries, true, true, false);
|
||||
foreach (var a in entries.Where(e => e.IsInSceneList || e.IsInResources))
|
||||
{
|
||||
if (!playerDataSchema.IncludeBuildSettingsScenes && a.IsInSceneList)
|
||||
continue;
|
||||
if (!playerDataSchema.IncludeResourcesFolders && a.IsInResources)
|
||||
continue;
|
||||
a.CreateCatalogEntries(locations, false, a.IsScene ? "" : typeof(LegacyResourcesProvider).FullName, null, null, providerTypes);
|
||||
if (!a.IsScene)
|
||||
needsLegacyProvider = true;
|
||||
}
|
||||
}
|
||||
|
||||
return needsLegacyProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method for deleting files.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to delete.</param>
|
||||
protected static void DeleteFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to write a file. The directory will be created if it does not exist.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to write.</param>
|
||||
/// <param name="content">The content of the file.</param>
|
||||
/// <param name="registry">The file registry used to track all produced artifacts.</param>
|
||||
/// <returns>True if the file was written.</returns>
|
||||
protected internal static bool WriteFile(string path, byte[] content, FileRegistry registry)
|
||||
{
|
||||
try
|
||||
{
|
||||
registry.AddFile(path);
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllBytes(path, content);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
registry.RemoveFile(path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to write a file. The directory will be created if it does not exist.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to write.</param>
|
||||
/// <param name="content">The content of the file.</param>
|
||||
/// <param name="registry">The file registry used to track all produced artifacts.</param>
|
||||
/// <returns>True if the file was written.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to clean up any cached data created by this builder.
|
||||
/// </summary>
|
||||
public virtual void ClearCachedData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the data is built for the given builder.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the data is built. Returns false otherwise.</returns>
|
||||
public virtual bool IsDataBuilt()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8559dec58f92398448ea2ea83fd63c3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Build.DataBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Only saves the guid of the settings asset to PlayerPrefs. All catalog data is generated directly from the settings as needed.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = nameof(BuildScriptFastMode) + ".asset", menuName = "Addressables/Content Builders/Use Asset Database (fastest)")]
|
||||
public class BuildScriptFastMode : BuildScriptBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name
|
||||
{
|
||||
get { return "Use Asset Database (fastest)"; }
|
||||
}
|
||||
|
||||
private bool m_DataBuilt;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearCachedData()
|
||||
{
|
||||
m_DataBuilt = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsDataBuilt()
|
||||
{
|
||||
return m_DataBuilt;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string ProcessGroup(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanBuildData<T>()
|
||||
{
|
||||
return typeof(T).IsAssignableFrom(typeof(AddressablesPlayModeBuildResult));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override TResult BuildDataImplementation<TResult>(AddressablesDataBuilderInput builderInput)
|
||||
{
|
||||
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(builderInput.AddressableSettings, out var guid, out long _))
|
||||
{
|
||||
IDataBuilderResult res = new AddressablesPlayModeBuildResult() {Error = "Invalid Settings asset."};
|
||||
return (TResult)res;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerPrefs.SetString(Addressables.kAddressablesRuntimeDataPath, $"GUID:{guid}");
|
||||
IDataBuilderResult res = new AddressablesPlayModeBuildResult() {OutputPath = "", Duration = 0};
|
||||
m_DataBuilt = true;
|
||||
return (TResult)res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 88d21199f5d473f4db36845f2318f180
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e2e0ffa088c91d41a086d0b8cb16bdc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Build.DataBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses data built by BuildScriptPacked class. This script just sets up the correct variables and runs.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "BuildScriptPackedPlayMode.asset", menuName = "Addressables/Content Builders/Use Existing Build (requires built groups)")]
|
||||
public class BuildScriptPackedPlayMode : BuildScriptBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name
|
||||
{
|
||||
get { return "Use Existing Build (requires built groups)"; }
|
||||
}
|
||||
|
||||
private bool m_DataBuilt;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearCachedData()
|
||||
{
|
||||
m_DataBuilt = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsDataBuilt()
|
||||
{
|
||||
return m_DataBuilt;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanBuildData<T>()
|
||||
{
|
||||
return typeof(T).IsAssignableFrom(typeof(AddressablesPlayModeBuildResult));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override TResult BuildDataImplementation<TResult>(AddressablesDataBuilderInput builderInput)
|
||||
{
|
||||
var timer = new System.Diagnostics.Stopwatch();
|
||||
timer.Start();
|
||||
var settingsPath = Addressables.BuildPath + "/settings.json";
|
||||
var buildLogsPath = Addressables.BuildPath + "/buildLogs.json";
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
IDataBuilderResult resE = new AddressablesPlayModeBuildResult()
|
||||
{
|
||||
Error = "Player content must be built before entering play mode with packed data. This can be done from the Addressables window in the Build->Build Player Content menu command."
|
||||
};
|
||||
return (TResult)resE;
|
||||
}
|
||||
|
||||
var rtd = JsonUtility.FromJson<ResourceManagerRuntimeData>(File.ReadAllText(settingsPath));
|
||||
if (rtd == null)
|
||||
{
|
||||
IDataBuilderResult resE = new AddressablesPlayModeBuildResult()
|
||||
{
|
||||
Error = string.Format("Unable to load initialization data from path {0}. This can be done from the Addressables window in the Build->Build Player Content menu command.",
|
||||
settingsPath)
|
||||
};
|
||||
return (TResult)resE;
|
||||
}
|
||||
|
||||
PackedPlayModeBuildLogs buildLogs = new PackedPlayModeBuildLogs();
|
||||
BuildTarget dataBuildTarget = BuildTarget.NoTarget;
|
||||
if (!Enum.TryParse(rtd.BuildTarget, out dataBuildTarget))
|
||||
{
|
||||
buildLogs.RuntimeBuildLogs.Add(new PackedPlayModeBuildLogs.RuntimeBuildLog(LogType.Warning,
|
||||
$"Unable to parse build target from initialization data: '{rtd.BuildTarget}'."));
|
||||
}
|
||||
|
||||
else if (BuildPipeline.GetBuildTargetGroup(dataBuildTarget) != BuildTargetGroup.Standalone)
|
||||
{
|
||||
buildLogs.RuntimeBuildLogs.Add(new PackedPlayModeBuildLogs.RuntimeBuildLog(LogType.Warning,
|
||||
$"Asset bundles built with build target {dataBuildTarget} may not be compatible with running in the Editor."));
|
||||
}
|
||||
|
||||
if (buildLogs.RuntimeBuildLogs.Count > 0)
|
||||
File.WriteAllText(buildLogsPath, JsonUtility.ToJson(buildLogs));
|
||||
|
||||
//TODO: detect if the data that does exist is out of date..
|
||||
var runtimeSettingsPath = "{UnityEngine.AddressableAssets.Addressables.RuntimePath}/settings.json";
|
||||
PlayerPrefs.SetString(Addressables.kAddressablesRuntimeDataPath, runtimeSettingsPath);
|
||||
PlayerPrefs.SetString(Addressables.kAddressablesRuntimeBuildLogPath, buildLogsPath);
|
||||
IDataBuilderResult res = new AddressablesPlayModeBuildResult() {OutputPath = settingsPath, Duration = timer.Elapsed.TotalSeconds};
|
||||
m_DataBuilt = true;
|
||||
return (TResult)res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ad8c280d42ee0ed41a27db23b43dd2bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,437 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.AddressableAssets.Build.BuildPipelineTasks;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEditor.Build.Pipeline;
|
||||
using UnityEditor.Build.Pipeline.Interfaces;
|
||||
using UnityEditor.Build.Pipeline.Tasks;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
using UnityEngine.AddressableAssets.ResourceLocators;
|
||||
using UnityEngine.AddressableAssets.ResourceProviders;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders.Simulation;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Experimental;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Build.DataBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Build script for creating virtual asset bundle dat for running in the editor.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "BuildScriptVirtual.asset", menuName = "Addressables/Content Builders/Simulate Groups (advanced)")]
|
||||
public class BuildScriptVirtualMode : BuildScriptBase
|
||||
{
|
||||
protected internal const string kCatalogExt =
|
||||
#if ENABLE_BINARY_CATALOG
|
||||
".bin";
|
||||
#else
|
||||
".json";
|
||||
#endif
|
||||
/// <inheritdoc />
|
||||
public override string Name
|
||||
{
|
||||
get { return "Simulate Groups (advanced)"; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanBuildData<T>()
|
||||
{
|
||||
return typeof(T).IsAssignableFrom(typeof(AddressablesPlayModeBuildResult));
|
||||
}
|
||||
string m_PathSuffix = "";
|
||||
string GetCatalogPath(string relPath = "")
|
||||
{
|
||||
return $"{relPath}{Addressables.LibraryPath}catalog{m_PathSuffix}{kCatalogExt}";
|
||||
}
|
||||
|
||||
string GetSettingsPath(string relPath = "")
|
||||
{
|
||||
return $"{relPath}{Addressables.LibraryPath}settings{m_PathSuffix}.json";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearCachedData()
|
||||
{
|
||||
DeleteFile(GetCatalogPath());
|
||||
DeleteFile(GetSettingsPath());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsDataBuilt()
|
||||
{
|
||||
return File.Exists(GetCatalogPath()) && File.Exists(GetSettingsPath());
|
||||
}
|
||||
|
||||
List<ObjectInitializationData> m_ResourceProviderData;
|
||||
List<AssetBundleBuild> m_AllBundleInputDefinitions;
|
||||
Dictionary<string, VirtualAssetBundleRuntimeData> m_CreatedProviderIds;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override TResult BuildDataImplementation<TResult>(AddressablesDataBuilderInput builderInput)
|
||||
{
|
||||
TResult result = default(TResult);
|
||||
|
||||
var timer = new System.Diagnostics.Stopwatch();
|
||||
timer.Start();
|
||||
var aaSettings = builderInput.AddressableSettings;
|
||||
|
||||
m_PathSuffix = builderInput.PathSuffix;
|
||||
|
||||
|
||||
//gather entries
|
||||
var aaContext = new AddressableAssetsBuildContext
|
||||
{
|
||||
Settings = aaSettings,
|
||||
runtimeData = new ResourceManagerRuntimeData(),
|
||||
bundleToAssetGroup = new Dictionary<string, string>(),
|
||||
locations = new List<ContentCatalogDataEntry>(),
|
||||
providerTypes = new HashSet<Type>(),
|
||||
assetEntries = new List<AddressableAssetEntry>(),
|
||||
buildStartTime = DateTime.Now
|
||||
};
|
||||
m_AllBundleInputDefinitions = new List<AssetBundleBuild>();
|
||||
aaContext.runtimeData.BuildTarget = builderInput.Target.ToString();
|
||||
aaContext.runtimeData.ProfileEvents = ProjectConfigData.PostProfilerEvents;
|
||||
aaContext.runtimeData.LogResourceManagerExceptions = aaSettings.buildSettings.LogResourceManagerExceptions;
|
||||
aaContext.runtimeData.ProfileEvents = ProjectConfigData.PostProfilerEvents;
|
||||
aaContext.runtimeData.MaxConcurrentWebRequests = aaSettings.MaxConcurrentWebRequests;
|
||||
aaContext.runtimeData.CatalogRequestsTimeout = aaSettings.CatalogRequestsTimeout;
|
||||
aaContext.runtimeData.CatalogLocations.Add(new ResourceLocationData(
|
||||
new[] { ResourceManagerRuntimeData.kCatalogAddress },
|
||||
GetCatalogPath("file://{UnityEngine.Application.dataPath}/../"),
|
||||
typeof(ContentCatalogProvider), typeof(ContentCatalogData)));
|
||||
aaContext.runtimeData.AddressablesVersion = PackageManager.PackageInfo.FindForAssembly(typeof(Addressables).Assembly)?.version;
|
||||
m_CreatedProviderIds = new Dictionary<string, VirtualAssetBundleRuntimeData>();
|
||||
m_ResourceProviderData = new List<ObjectInitializationData>();
|
||||
|
||||
var errorString = ProcessAllGroups(aaContext);
|
||||
if (!string.IsNullOrEmpty(errorString))
|
||||
result = AddressableAssetBuildResult.CreateResult<TResult>(null, 0, errorString);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = DoBuild<TResult>(builderInput, aaSettings, aaContext);
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
result.Duration = timer.Elapsed.TotalSeconds;
|
||||
return result;
|
||||
}
|
||||
|
||||
TResult DoBuild<TResult>(AddressablesDataBuilderInput builderInput, AddressableAssetSettings aaSettings, AddressableAssetsBuildContext aaContext) where TResult : IDataBuilderResult
|
||||
{
|
||||
if (m_AllBundleInputDefinitions.Count > 0)
|
||||
{
|
||||
if (!BuildUtility.CheckModifiedScenesAndAskToSave())
|
||||
return AddressableAssetBuildResult.CreateResult<TResult>(null, 0, "Unsaved scenes");
|
||||
|
||||
var buildTarget = builderInput.Target;
|
||||
var buildTargetGroup = builderInput.TargetGroup;
|
||||
var buildParams = new AddressableAssetsBundleBuildParameters(aaSettings, aaContext.bundleToAssetGroup, buildTarget, buildTargetGroup, aaSettings.buildSettings.bundleBuildPath);
|
||||
var builtinShaderBundleName = aaSettings.DefaultGroup.Name.ToLower().Replace(" ", "").Replace('\\', '/').Replace("//", "/") + "_unitybuiltinshaders.bundle";
|
||||
var buildTasks = RuntimeDataBuildTasks(aaSettings.buildSettings.compileScriptsInVirtualMode, builtinShaderBundleName);
|
||||
ExtractDataTask extractData = new ExtractDataTask();
|
||||
buildTasks.Add(extractData);
|
||||
|
||||
string aaPath = aaSettings.AssetPath;
|
||||
IBundleBuildResults results;
|
||||
var exitCode = ContentPipeline.BuildAssetBundles(buildParams, new BundleBuildContent(m_AllBundleInputDefinitions), out results, buildTasks, aaContext);
|
||||
|
||||
if (exitCode < ReturnCode.Success)
|
||||
return AddressableAssetBuildResult.CreateResult<TResult>(null, 0, "SBP Error" + exitCode);
|
||||
|
||||
if (aaSettings == null && !string.IsNullOrEmpty(aaPath))
|
||||
aaSettings = AssetDatabase.LoadAssetAtPath<AddressableAssetSettings>(aaPath);
|
||||
}
|
||||
|
||||
var bundledAssets = new Dictionary<object, HashSet<string>>();
|
||||
foreach (var loc in aaContext.locations)
|
||||
{
|
||||
if (loc.Dependencies != null && loc.Dependencies.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < loc.Dependencies.Count; i++)
|
||||
{
|
||||
var dep = loc.Dependencies[i];
|
||||
HashSet<string> assetsInBundle;
|
||||
if (!bundledAssets.TryGetValue(dep, out assetsInBundle))
|
||||
bundledAssets.Add(dep, assetsInBundle = new HashSet<string>());
|
||||
if (i == 0 && !assetsInBundle.Contains(loc.InternalId)) //only add the asset to the first bundle...
|
||||
assetsInBundle.Add(loc.InternalId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bd in bundledAssets)
|
||||
{
|
||||
AddressableAssetGroup group = aaSettings.DefaultGroup;
|
||||
string groupGuid;
|
||||
if (aaContext.bundleToAssetGroup.TryGetValue(bd.Key as string, out groupGuid))
|
||||
group = aaSettings.FindGroup(g => g.Guid == groupGuid);
|
||||
|
||||
var schema = group.GetSchema<BundledAssetGroupSchema>();
|
||||
if (schema != null)
|
||||
{
|
||||
var bundleLocData = aaContext.locations.First(s => s.Keys[0] == bd.Key);
|
||||
var isLocalBundle = IsInternalIdLocal(bundleLocData.InternalId);
|
||||
uint crc = (uint)UnityEngine.Random.Range(0, int.MaxValue);
|
||||
var hash = Guid.NewGuid().ToString();
|
||||
|
||||
string originalBundleName = bd.Key as string;
|
||||
string newBundleName = BuildUtility.GetNameWithHashNaming(schema.BundleNaming, hash, originalBundleName);
|
||||
bundleLocData.InternalId = bundleLocData.InternalId.Remove(bundleLocData.InternalId.Length - originalBundleName.Length) + newBundleName;
|
||||
var abb = m_AllBundleInputDefinitions.FirstOrDefault(a => a.assetBundleName == originalBundleName);
|
||||
var virtualBundleName = AddressablesRuntimeProperties.EvaluateString(bundleLocData.InternalId);
|
||||
var bundleData = new VirtualAssetBundle(virtualBundleName, isLocalBundle, crc, hash);
|
||||
|
||||
long dataSize = 0;
|
||||
long headerSize = 0;
|
||||
foreach (var a in bd.Value)
|
||||
{
|
||||
var i = Array.IndexOf(abb.addressableNames, a);
|
||||
var assetPath = abb.assetNames[i];
|
||||
var size = ComputeSize(assetPath);
|
||||
var vab = new VirtualAssetBundleEntry(a, size);
|
||||
vab.m_AssetPath = assetPath;
|
||||
bundleData.Assets.Add(vab);
|
||||
dataSize += size;
|
||||
headerSize += a.Length * 5; //assume 5x path length overhead size per item, probably much less
|
||||
}
|
||||
|
||||
if (bd.Value.Count == 0)
|
||||
{
|
||||
dataSize = 100 * 1024;
|
||||
headerSize = 1024;
|
||||
}
|
||||
|
||||
bundleData.SetSize(dataSize, headerSize);
|
||||
|
||||
|
||||
var requestOptions = new VirtualAssetBundleRequestOptions
|
||||
{
|
||||
Crc = schema.UseAssetBundleCrc ? crc : 0,
|
||||
Hash = schema.UseAssetBundleCache ? hash : "",
|
||||
ChunkedTransfer = schema.ChunkedTransfer,
|
||||
RedirectLimit = schema.RedirectLimit,
|
||||
RetryCount = schema.RetryCount,
|
||||
Timeout = schema.Timeout,
|
||||
BundleName = Path.GetFileName(bundleLocData.InternalId),
|
||||
AssetLoadMode = schema.AssetLoadMode,
|
||||
BundleSize = dataSize + headerSize
|
||||
};
|
||||
bundleLocData.Data = requestOptions;
|
||||
|
||||
var bundleProviderId = schema.GetBundleCachedProviderId();
|
||||
var virtualBundleRuntimeData = m_CreatedProviderIds[bundleProviderId];
|
||||
virtualBundleRuntimeData.AssetBundles.Add(bundleData);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kvp in m_CreatedProviderIds)
|
||||
{
|
||||
if (kvp.Value != null)
|
||||
{
|
||||
var bundleProviderData = ObjectInitializationData.CreateSerializedInitializationData<VirtualAssetBundleProvider>(kvp.Key, kvp.Value);
|
||||
m_ResourceProviderData.Add(bundleProviderData);
|
||||
}
|
||||
}
|
||||
#if ENABLE_BINARY_CATALOG
|
||||
var contentCatalog = new ContentCatalogData(ResourceManagerRuntimeData.kCatalogAddress);
|
||||
|
||||
contentCatalog.ResourceProviderData.AddRange(m_ResourceProviderData);
|
||||
foreach (var t in aaContext.providerTypes)
|
||||
contentCatalog.ResourceProviderData.Add(ObjectInitializationData.CreateSerializedInitializationData(t));
|
||||
|
||||
contentCatalog.InstanceProviderData = ObjectInitializationData.CreateSerializedInitializationData(instanceProviderType.Value);
|
||||
contentCatalog.SceneProviderData = ObjectInitializationData.CreateSerializedInitializationData(sceneProviderType.Value);
|
||||
|
||||
contentCatalog.SetData(aaContext.locations.OrderBy(f => f.InternalId).ToList());
|
||||
//save catalog
|
||||
WriteFile(GetCatalogPath(), contentCatalog.SerializeToByteArray(), builderInput.Registry);
|
||||
|
||||
#else
|
||||
var contentCatalog = new ContentCatalogData(ResourceManagerRuntimeData.kCatalogAddress);
|
||||
contentCatalog.SetData(aaContext.locations.OrderBy(f => f.InternalId).ToList());
|
||||
|
||||
contentCatalog.ResourceProviderData.AddRange(m_ResourceProviderData);
|
||||
foreach (var t in aaContext.providerTypes)
|
||||
contentCatalog.ResourceProviderData.Add(ObjectInitializationData.CreateSerializedInitializationData(t));
|
||||
|
||||
contentCatalog.InstanceProviderData = ObjectInitializationData.CreateSerializedInitializationData(instanceProviderType.Value);
|
||||
contentCatalog.SceneProviderData = ObjectInitializationData.CreateSerializedInitializationData(sceneProviderType.Value);
|
||||
|
||||
//save catalog
|
||||
WriteFile(GetCatalogPath(), JsonUtility.ToJson(contentCatalog), builderInput.Registry);
|
||||
#endif
|
||||
|
||||
|
||||
foreach (var io in aaSettings.InitializationObjects)
|
||||
{
|
||||
if (io is IObjectInitializationDataProvider)
|
||||
aaContext.runtimeData.InitializationObjects.Add((io as IObjectInitializationDataProvider).CreateObjectInitializationData());
|
||||
}
|
||||
|
||||
var settingsPath = GetSettingsPath();
|
||||
WriteFile(settingsPath, JsonUtility.ToJson(aaContext.runtimeData), builderInput.Registry);
|
||||
|
||||
//inform runtime of the init data path
|
||||
var runtimeSettingsPath = GetSettingsPath("file://{UnityEngine.Application.dataPath}/../");
|
||||
PlayerPrefs.SetString(Addressables.kAddressablesRuntimeDataPath, runtimeSettingsPath);
|
||||
var result = AddressableAssetBuildResult.CreateResult<TResult>(settingsPath, aaContext.locations.Count);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string ProcessGroup(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext)
|
||||
{
|
||||
var errorString = string.Empty;
|
||||
PlayerDataGroupSchema playerSchema = assetGroup.GetSchema<PlayerDataGroupSchema>();
|
||||
if (playerSchema != null)
|
||||
{
|
||||
if (CreateLocationsForPlayerData(playerSchema, assetGroup, aaContext.locations, aaContext.providerTypes))
|
||||
{
|
||||
if (!m_CreatedProviderIds.ContainsKey(typeof(LegacyResourcesProvider).Name))
|
||||
{
|
||||
m_CreatedProviderIds.Add(typeof(LegacyResourcesProvider).Name, null);
|
||||
m_ResourceProviderData.Add(ObjectInitializationData.CreateSerializedInitializationData(typeof(LegacyResourcesProvider)));
|
||||
}
|
||||
}
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
var schema = assetGroup.GetSchema<BundledAssetGroupSchema>();
|
||||
if (schema == null)
|
||||
return errorString;
|
||||
|
||||
var bundledProviderId = schema.GetBundleCachedProviderId();
|
||||
var assetProviderId = schema.GetAssetCachedProviderId();
|
||||
if (!m_CreatedProviderIds.ContainsKey(bundledProviderId))
|
||||
{
|
||||
//TODO: pull from schema instead of ProjectConfigData
|
||||
var virtualBundleRuntimeData = new VirtualAssetBundleRuntimeData(ProjectConfigData.LocalLoadSpeed, ProjectConfigData.RemoteLoadSpeed);
|
||||
//save virtual runtime data to collect assets into virtual bundles
|
||||
m_CreatedProviderIds.Add(bundledProviderId, virtualBundleRuntimeData);
|
||||
}
|
||||
|
||||
if (!m_CreatedProviderIds.ContainsKey(assetProviderId))
|
||||
{
|
||||
m_CreatedProviderIds.Add(assetProviderId, null);
|
||||
|
||||
var assetProviderData = ObjectInitializationData.CreateSerializedInitializationData<VirtualBundledAssetProvider>(assetProviderId);
|
||||
m_ResourceProviderData.Add(assetProviderData);
|
||||
}
|
||||
|
||||
|
||||
var bundleInputDefs = new List<AssetBundleBuild>();
|
||||
List<AddressableAssetEntry> list = BuildScriptPackedMode.PrepGroupBundlePacking(assetGroup, bundleInputDefs, schema);
|
||||
aaContext.assetEntries.AddRange(list);
|
||||
for (int i = 0; i < bundleInputDefs.Count; i++)
|
||||
{
|
||||
if (aaContext.bundleToAssetGroup.ContainsKey(bundleInputDefs[i].assetBundleName))
|
||||
{
|
||||
var bid = bundleInputDefs[i];
|
||||
int count = 1;
|
||||
var newName = bid.assetBundleName;
|
||||
while (aaContext.bundleToAssetGroup.ContainsKey(newName) && count < 1000)
|
||||
newName = bid.assetBundleName.Replace(".bundle", string.Format("{0}.bundle", count++));
|
||||
bundleInputDefs[i] = new AssetBundleBuild
|
||||
{assetBundleName = newName, addressableNames = bid.addressableNames, assetBundleVariant = bid.assetBundleVariant, assetNames = bid.assetNames};
|
||||
}
|
||||
|
||||
aaContext.bundleToAssetGroup.Add(bundleInputDefs[i].assetBundleName, assetGroup.Guid);
|
||||
}
|
||||
|
||||
m_AllBundleInputDefinitions.AddRange(bundleInputDefs);
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
static bool IsInternalIdLocal(string path)
|
||||
{
|
||||
return path.StartsWith("{UnityEngine.AddressableAssets.Addressables.RuntimePath}", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
static string OutputLibraryPathForAsset(string a)
|
||||
{
|
||||
var guid = AssetDatabase.AssetPathToGUID(a);
|
||||
var legacyPath = string.Format("Library/metadata/{0}{1}/{2}", guid[0], guid[1], guid);
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
var artifactID = Experimental.AssetDatabaseExperimental.ProduceArtifact(new ArtifactKey(new GUID(guid)));
|
||||
if (Experimental.AssetDatabaseExperimental.GetArtifactPaths(artifactID, out var paths))
|
||||
return Path.GetFullPath(paths[0]);
|
||||
else
|
||||
legacyPath = String.Empty;
|
||||
#elif UNITY_2020_1_OR_NEWER
|
||||
var hash = Experimental.AssetDatabaseExperimental.GetArtifactHash(guid);
|
||||
if (Experimental.AssetDatabaseExperimental.GetArtifactPaths(hash, out var paths))
|
||||
return Path.GetFullPath(paths[0]);
|
||||
else
|
||||
legacyPath = String.Empty; // legacy path is never valid in 2020.1+
|
||||
#else
|
||||
if (IsAssetDatabaseV2Enabled()) // AssetDatabase V2 is optional in 2019.3 and 2019.4
|
||||
{
|
||||
var hash = Experimental.AssetDatabaseExperimental.GetArtifactHash(guid);
|
||||
if (Experimental.AssetDatabaseExperimental.GetArtifactPaths(hash, out var paths))
|
||||
return Path.GetFullPath(paths[0]);
|
||||
}
|
||||
#endif
|
||||
return legacyPath;
|
||||
}
|
||||
|
||||
static bool IsAssetDatabaseV2Enabled()
|
||||
{
|
||||
// method is internal
|
||||
var methodInfo = typeof(AssetDatabase).GetMethod("IsV2Enabled", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
return methodInfo != null && (bool)methodInfo.Invoke(null, null);
|
||||
}
|
||||
|
||||
static long ComputeSize(string a)
|
||||
{
|
||||
var path = OutputLibraryPathForAsset(a);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return 1024 * 1024;
|
||||
}
|
||||
|
||||
return new FileInfo(path).Length;
|
||||
}
|
||||
|
||||
static IList<IBuildTask> RuntimeDataBuildTasks(bool compileScripts, string builtinShaderBundleName)
|
||||
{
|
||||
var buildTasks = new List<IBuildTask>();
|
||||
|
||||
// Setup
|
||||
buildTasks.Add(new SwitchToBuildPlatform());
|
||||
buildTasks.Add(new RebuildSpriteAtlasCache());
|
||||
|
||||
// Player Scripts
|
||||
if (compileScripts)
|
||||
{
|
||||
buildTasks.Add(new BuildPlayerScripts());
|
||||
buildTasks.Add(new PostScriptsCallback());
|
||||
}
|
||||
|
||||
// Dependency
|
||||
buildTasks.Add(new PreviewSceneDependencyData());
|
||||
buildTasks.Add(new CalculateAssetDependencyData());
|
||||
buildTasks.Add(new StripUnusedSpriteSources());
|
||||
buildTasks.Add(new CreateBuiltInShadersBundle(builtinShaderBundleName));
|
||||
buildTasks.Add(new PostDependencyCallback());
|
||||
|
||||
// Packing
|
||||
buildTasks.Add(new GenerateBundlePacking());
|
||||
buildTasks.Add(new UpdateBundleObjectLayout());
|
||||
buildTasks.Add(new GenerateLocationListsTask());
|
||||
buildTasks.Add(new PostPackingCallback());
|
||||
|
||||
return buildTasks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bb0e4994b34add1409fd8ccaf4a82de5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Add table
Add a link
Reference in a new issue