mirror of
https://github.com/muskit/H3VR-TNH-Quality-of-Life-Improvements.git
synced 2026-06-03 04:34:26 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,766 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Assertions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using AssetBundleBrowser.AssetBundleDataSource;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class holding model data for Asset Bundle Browser tool. Data in Model is read from DataSource, but is not pushed.
|
||||
///
|
||||
/// If not using a custom DataSource, then the data comes from the AssetDatabase. If you wish to alter the data from code,
|
||||
/// you should just push changes to the AssetDatabase then tell the Model to Rebuild(). If needed, you can also loop over
|
||||
/// Update() until it returns true to force all sub-items to refresh.
|
||||
///
|
||||
/// </summary>
|
||||
public static class Model
|
||||
{
|
||||
private const string k_NewBundleBaseName = "newbundle";
|
||||
private const string k_NewVariantBaseName = "newvariant";
|
||||
internal static /*const*/ Color k_LightGrey = Color.grey * 1.5f;
|
||||
|
||||
private static ABDataSource s_DataSource;
|
||||
private static BundleFolderConcreteInfo s_RootLevelBundles = new BundleFolderConcreteInfo("", null);
|
||||
private static List<ABMoveData> s_MoveData = new List<ABMoveData>();
|
||||
private static List<BundleInfo> s_BundlesToUpdate = new List<BundleInfo>();
|
||||
private static Dictionary<string, AssetInfo> s_GlobalAssetList = new Dictionary<string, AssetInfo>();
|
||||
private static Dictionary<string, HashSet<string>> s_DependencyTracker = new Dictionary<string, HashSet<string>>();
|
||||
|
||||
private static bool s_InErrorState = false;
|
||||
private const string k_DefaultEmptyMessage = "Drag assets here or right-click to begin creating bundles.";
|
||||
private const string k_ProblemEmptyMessage = "There was a problem parsing the list of bundles. See console.";
|
||||
private static string s_EmptyMessageString;
|
||||
|
||||
private static Texture2D s_folderIcon = null;
|
||||
private static Texture2D s_bundleIcon = null;
|
||||
private static Texture2D s_sceneIcon = null;
|
||||
|
||||
/// <summary>
|
||||
/// If using a custom source of asset bundles, you can implement your own ABDataSource and set it here as the active
|
||||
/// DataSource. This will allow you to use the Browser with data that you provide.
|
||||
///
|
||||
/// If no custom DataSource is provided, then the Browser will create one that feeds off of and into the
|
||||
/// AssetDatabase.
|
||||
///
|
||||
/// </summary>
|
||||
public static ABDataSource DataSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_DataSource == null) s_DataSource = new AssetDatabaseABDataSource();
|
||||
return s_DataSource;
|
||||
}
|
||||
set { s_DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update will loop over bundles that need updating and update them. It will only update one bundle
|
||||
/// per frame and will continue on the same bundle next frame until that bundle is marked as doneUpdating.
|
||||
/// By default, this will cause a very slow collection of dependency data as it will only update one bundle per
|
||||
/// </summary>
|
||||
public static bool Update()
|
||||
{
|
||||
bool shouldRepaint = false;
|
||||
ExecuteAssetMove(false); //this should never do anything. just a safety check.
|
||||
|
||||
//TODO - look into EditorApplication callback functions.
|
||||
|
||||
int size = s_BundlesToUpdate.Count;
|
||||
if (size > 0)
|
||||
{
|
||||
s_BundlesToUpdate[size - 1].Update();
|
||||
s_BundlesToUpdate.RemoveAll(item => item.doneUpdating == true);
|
||||
if (s_BundlesToUpdate.Count == 0)
|
||||
{
|
||||
shouldRepaint = true;
|
||||
foreach (BundleInfo bundle in s_RootLevelBundles.GetChildList()) bundle.RefreshDupeAssetWarning();
|
||||
}
|
||||
}
|
||||
|
||||
return shouldRepaint;
|
||||
}
|
||||
|
||||
internal static void ForceReloadData(TreeView tree)
|
||||
{
|
||||
s_InErrorState = false;
|
||||
Rebuild();
|
||||
tree.Reload();
|
||||
bool doneUpdating = s_BundlesToUpdate.Count == 0;
|
||||
|
||||
EditorUtility.DisplayProgressBar("Updating Bundles", "", 0);
|
||||
int fullBundleCount = s_BundlesToUpdate.Count;
|
||||
while (!doneUpdating && !s_InErrorState)
|
||||
{
|
||||
int currCount = s_BundlesToUpdate.Count;
|
||||
EditorUtility.DisplayProgressBar("Updating Bundles", s_BundlesToUpdate[currCount - 1].displayName, (float) (fullBundleCount - currCount) / (float) fullBundleCount);
|
||||
doneUpdating = Update();
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears and rebuilds model data.
|
||||
/// </summary>
|
||||
public static void Rebuild()
|
||||
{
|
||||
s_RootLevelBundles = new BundleFolderConcreteInfo("", null);
|
||||
s_MoveData = new List<ABMoveData>();
|
||||
s_BundlesToUpdate = new List<BundleInfo>();
|
||||
s_GlobalAssetList = new Dictionary<string, AssetInfo>();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
internal static void AddBundlesToUpdate(IEnumerable<BundleInfo> bundles)
|
||||
{
|
||||
foreach (BundleInfo bundle in bundles)
|
||||
{
|
||||
bundle.ForceNeedUpdate();
|
||||
s_BundlesToUpdate.Add(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Refresh()
|
||||
{
|
||||
s_EmptyMessageString = k_ProblemEmptyMessage;
|
||||
if (s_InErrorState)
|
||||
return;
|
||||
|
||||
string[] bundleList = ValidateBundleList();
|
||||
if (bundleList != null)
|
||||
{
|
||||
s_EmptyMessageString = k_DefaultEmptyMessage;
|
||||
foreach (string bundleName in bundleList) AddBundleToModel(bundleName);
|
||||
AddBundlesToUpdate(s_RootLevelBundles.GetChildList());
|
||||
}
|
||||
|
||||
if (s_InErrorState)
|
||||
{
|
||||
s_RootLevelBundles = new BundleFolderConcreteInfo("", null);
|
||||
s_EmptyMessageString = k_ProblemEmptyMessage;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string[] ValidateBundleList()
|
||||
{
|
||||
string[] bundleList = DataSource.GetAllAssetBundleNames();
|
||||
bool valid = true;
|
||||
HashSet<string> bundleSet = new HashSet<string>();
|
||||
int index = 0;
|
||||
bool attemptedBundleReset = false;
|
||||
while (index < bundleList.Length)
|
||||
{
|
||||
string name = bundleList[index];
|
||||
if (!bundleSet.Add(name))
|
||||
{
|
||||
LogError("Two bundles share the same name: " + name);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
int lastDot = name.LastIndexOf('.');
|
||||
if (lastDot > -1)
|
||||
{
|
||||
string bunName = name.Substring(0, lastDot);
|
||||
int extraDot = bunName.LastIndexOf('.');
|
||||
if (extraDot > -1)
|
||||
{
|
||||
if (attemptedBundleReset)
|
||||
{
|
||||
string message = "Bundle name '" + bunName + "' contains a period.";
|
||||
message += " Internally Unity keeps 'bundleName' and 'variantName' separate, but externally treat them as 'bundleName.variantName'.";
|
||||
message += " If a bundleName contains a period, the build will (probably) succeed, but this tool cannot tell which portion is bundle and which portion is variant.";
|
||||
LogError(message);
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!DataSource.IsReadOnly()) DataSource.RemoveUnusedAssetBundleNames();
|
||||
index = 0;
|
||||
bundleSet.Clear();
|
||||
bundleList = DataSource.GetAllAssetBundleNames();
|
||||
attemptedBundleReset = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (bundleList.Contains(bunName))
|
||||
{
|
||||
//there is a bundle.none and a bundle.variant coexisting. Need to fix that or return an error.
|
||||
if (attemptedBundleReset)
|
||||
{
|
||||
valid = false;
|
||||
string message = "Bundle name '" + bunName + "' exists without a variant as well as with variant '" + name.Substring(lastDot + 1) + "'.";
|
||||
message += " That is an illegal state that will not build and must be cleaned up.";
|
||||
LogError(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!DataSource.IsReadOnly()) DataSource.RemoveUnusedAssetBundleNames();
|
||||
index = 0;
|
||||
bundleSet.Clear();
|
||||
bundleList = DataSource.GetAllAssetBundleNames();
|
||||
attemptedBundleReset = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (valid)
|
||||
return bundleList;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static bool BundleListIsEmpty()
|
||||
{
|
||||
return s_RootLevelBundles.GetChildList().Count() == 0;
|
||||
}
|
||||
|
||||
internal static string GetEmptyMessage()
|
||||
{
|
||||
return s_EmptyMessageString;
|
||||
}
|
||||
|
||||
internal static BundleInfo CreateEmptyBundle(BundleFolderInfo folder = null, string newName = null)
|
||||
{
|
||||
if (folder as BundleVariantFolderInfo != null)
|
||||
return CreateEmptyVariant(folder as BundleVariantFolderInfo);
|
||||
|
||||
folder = folder == null ? s_RootLevelBundles : folder;
|
||||
string name = GetUniqueName(folder, newName);
|
||||
BundleNameData nameData;
|
||||
nameData = new BundleNameData(folder.m_Name.bundleName, name);
|
||||
return AddBundleToFolder(folder, nameData);
|
||||
}
|
||||
|
||||
internal static BundleInfo CreateEmptyVariant(BundleVariantFolderInfo folder)
|
||||
{
|
||||
string name = GetUniqueName(folder, k_NewVariantBaseName);
|
||||
string variantName = folder.m_Name.bundleName + "." + name;
|
||||
BundleNameData nameData = new BundleNameData(variantName);
|
||||
return AddBundleToFolder(folder.parent, nameData);
|
||||
}
|
||||
|
||||
internal static BundleFolderInfo CreateEmptyBundleFolder(BundleFolderConcreteInfo folder = null)
|
||||
{
|
||||
folder = folder == null ? s_RootLevelBundles : folder;
|
||||
string name = GetUniqueName(folder) + "/dummy";
|
||||
BundleNameData nameData = new BundleNameData(folder.m_Name.bundleName, name);
|
||||
return AddFoldersToBundle(s_RootLevelBundles, nameData);
|
||||
}
|
||||
|
||||
private static BundleInfo AddBundleToModel(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
BundleNameData nameData = new BundleNameData(name);
|
||||
|
||||
BundleFolderInfo folder = AddFoldersToBundle(s_RootLevelBundles, nameData);
|
||||
BundleInfo currInfo = AddBundleToFolder(folder, nameData);
|
||||
|
||||
return currInfo;
|
||||
}
|
||||
|
||||
private static BundleFolderConcreteInfo AddFoldersToBundle(BundleFolderInfo root, BundleNameData nameData)
|
||||
{
|
||||
BundleInfo currInfo = root;
|
||||
BundleFolderConcreteInfo folder = currInfo as BundleFolderConcreteInfo;
|
||||
int size = nameData.pathTokens.Count;
|
||||
for (int index = 0; index < size; index++)
|
||||
if (folder != null)
|
||||
{
|
||||
currInfo = folder.GetChild(nameData.pathTokens[index]);
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleFolderConcreteInfo(nameData.pathTokens, index + 1, folder);
|
||||
folder.AddChild(currInfo);
|
||||
}
|
||||
|
||||
folder = currInfo as BundleFolderConcreteInfo;
|
||||
if (folder == null)
|
||||
{
|
||||
s_InErrorState = true;
|
||||
LogFolderAndBundleNameConflict(currInfo.m_Name.fullNativeName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return currInfo as BundleFolderConcreteInfo;
|
||||
}
|
||||
|
||||
private static void LogFolderAndBundleNameConflict(string name)
|
||||
{
|
||||
string message = "Bundle '";
|
||||
message += name;
|
||||
message += "' has a name conflict with a bundle-folder.";
|
||||
message += "Display of bundle data and building of bundles will not work.";
|
||||
message += "\nDetails: If you name a bundle 'x/y', then the result of your build will be a bundle named 'y' in a folder named 'x'. You thus cannot also have a bundle named 'x' at the same level as the folder named 'x'.";
|
||||
LogError(message);
|
||||
}
|
||||
|
||||
private static BundleInfo AddBundleToFolder(BundleFolderInfo root, BundleNameData nameData)
|
||||
{
|
||||
BundleInfo currInfo = root.GetChild(nameData.shortName);
|
||||
if (!string.IsNullOrEmpty(nameData.variant))
|
||||
{
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleVariantFolderInfo(nameData.bundleName, root);
|
||||
root.AddChild(currInfo);
|
||||
}
|
||||
|
||||
BundleVariantFolderInfo folder = currInfo as BundleVariantFolderInfo;
|
||||
if (folder == null)
|
||||
{
|
||||
string message = "Bundle named " + nameData.shortName;
|
||||
message += " exists both as a standard bundle, and a bundle with variants. ";
|
||||
message += "This message is not supported for display or actual bundle building. ";
|
||||
message += "You must manually fix bundle naming in the inspector.";
|
||||
|
||||
LogError(message);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
currInfo = folder.GetChild(nameData.variant);
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleVariantDataInfo(nameData.fullNativeName, folder);
|
||||
folder.AddChild(currInfo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleDataInfo(nameData.fullNativeName, root);
|
||||
root.AddChild(currInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
BundleDataInfo dataInfo = currInfo as BundleDataInfo;
|
||||
if (dataInfo == null)
|
||||
{
|
||||
s_InErrorState = true;
|
||||
LogFolderAndBundleNameConflict(nameData.fullNativeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currInfo;
|
||||
}
|
||||
|
||||
private static string GetUniqueName(BundleFolderInfo folder, string suggestedName = null)
|
||||
{
|
||||
suggestedName = suggestedName == null ? k_NewBundleBaseName : suggestedName;
|
||||
string name = suggestedName;
|
||||
int index = 1;
|
||||
bool foundExisting = folder.GetChild(name) != null;
|
||||
while (foundExisting)
|
||||
{
|
||||
name = suggestedName + index;
|
||||
index++;
|
||||
foundExisting = folder.GetChild(name) != null;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
internal static BundleTreeItem CreateBundleTreeView()
|
||||
{
|
||||
return s_RootLevelBundles.CreateTreeView(-1);
|
||||
}
|
||||
|
||||
internal static AssetTreeItem CreateAssetListTreeView(IEnumerable<BundleInfo> selectedBundles)
|
||||
{
|
||||
AssetTreeItem root = new AssetTreeItem();
|
||||
if (selectedBundles != null)
|
||||
foreach (BundleInfo bundle in selectedBundles)
|
||||
bundle.AddAssetsToNode(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
internal static bool HandleBundleRename(BundleTreeItem item, string newName)
|
||||
{
|
||||
BundleNameData originalName = new BundleNameData(item.bundle.m_Name.fullNativeName);
|
||||
|
||||
int findDot = newName.LastIndexOf('.');
|
||||
int findSlash = newName.LastIndexOf('/');
|
||||
int findBSlash = newName.LastIndexOf('\\');
|
||||
if (findDot == 0 || findSlash == 0 || findBSlash == 0)
|
||||
return false; //can't start a bundle with a / or .
|
||||
|
||||
bool result = item.bundle.HandleRename(newName, 0);
|
||||
|
||||
if (findDot > 0 || findSlash > 0 || findBSlash > 0) item.bundle.parent.HandleChildRename(newName, string.Empty);
|
||||
|
||||
ExecuteAssetMove();
|
||||
|
||||
BundleInfo node = FindBundle(originalName);
|
||||
if (node != null)
|
||||
{
|
||||
string message = "Failed to rename bundle named: ";
|
||||
message += originalName.fullNativeName;
|
||||
message += ". Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";
|
||||
Debug.LogError(message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void HandleBundleReparent(IEnumerable<BundleInfo> bundles, BundleFolderInfo parent)
|
||||
{
|
||||
parent = parent == null ? s_RootLevelBundles : parent;
|
||||
foreach (BundleInfo bundle in bundles) bundle.HandleReparent(parent.m_Name.bundleName, parent);
|
||||
ExecuteAssetMove();
|
||||
}
|
||||
|
||||
internal static void HandleBundleMerge(IEnumerable<BundleInfo> bundles, BundleDataInfo target)
|
||||
{
|
||||
foreach (BundleInfo bundle in bundles) bundle.HandleDelete(true, target.m_Name.bundleName, target.m_Name.variant);
|
||||
ExecuteAssetMove();
|
||||
}
|
||||
|
||||
internal static void HandleBundleDelete(IEnumerable<BundleInfo> bundles)
|
||||
{
|
||||
List<BundleNameData> nameList = new List<BundleNameData>();
|
||||
foreach (BundleInfo bundle in bundles)
|
||||
{
|
||||
nameList.Add(bundle.m_Name);
|
||||
bundle.HandleDelete(true);
|
||||
}
|
||||
|
||||
ExecuteAssetMove();
|
||||
|
||||
//check to see if any bundles are still there...
|
||||
foreach (BundleNameData name in nameList)
|
||||
{
|
||||
BundleInfo node = FindBundle(name);
|
||||
if (node != null)
|
||||
{
|
||||
string message = "Failed to delete bundle named: ";
|
||||
message += name.fullNativeName;
|
||||
message += ". Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";
|
||||
Debug.LogError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static BundleInfo FindBundle(BundleNameData name)
|
||||
{
|
||||
BundleInfo currNode = s_RootLevelBundles;
|
||||
foreach (string token in name.pathTokens)
|
||||
if (currNode is BundleFolderInfo)
|
||||
{
|
||||
currNode = (currNode as BundleFolderInfo).GetChild(token);
|
||||
if (currNode == null)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (currNode is BundleFolderInfo)
|
||||
{
|
||||
currNode = (currNode as BundleFolderInfo).GetChild(name.shortName);
|
||||
if (currNode is BundleVariantFolderInfo) currNode = (currNode as BundleVariantFolderInfo).GetChild(name.variant);
|
||||
return currNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static BundleInfo HandleDedupeBundles(IEnumerable<BundleInfo> bundles, bool onlyOverlappedAssets)
|
||||
{
|
||||
BundleInfo newBundle = CreateEmptyBundle();
|
||||
HashSet<string> dupeAssets = new HashSet<string>();
|
||||
HashSet<string> fullAssetList = new HashSet<string>();
|
||||
|
||||
//if they were just selected, then they may still be updating.
|
||||
bool doneUpdating = s_BundlesToUpdate.Count == 0;
|
||||
while (!doneUpdating)
|
||||
doneUpdating = Update();
|
||||
|
||||
foreach (BundleInfo bundle in bundles)
|
||||
foreach (AssetInfo asset in bundle.GetDependencies())
|
||||
if (onlyOverlappedAssets)
|
||||
{
|
||||
if (!fullAssetList.Add(asset.fullAssetName))
|
||||
dupeAssets.Add(asset.fullAssetName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (asset.IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))
|
||||
dupeAssets.Add(asset.fullAssetName);
|
||||
}
|
||||
|
||||
if (dupeAssets.Count == 0)
|
||||
return null;
|
||||
|
||||
MoveAssetToBundle(dupeAssets, newBundle.m_Name.bundleName, string.Empty);
|
||||
ExecuteAssetMove();
|
||||
return newBundle;
|
||||
}
|
||||
|
||||
internal static BundleInfo HandleConvertToVariant(BundleDataInfo bundle)
|
||||
{
|
||||
bundle.HandleDelete(true, bundle.m_Name.bundleName, k_NewVariantBaseName);
|
||||
ExecuteAssetMove();
|
||||
BundleVariantFolderInfo root = bundle.parent.GetChild(bundle.m_Name.shortName) as BundleVariantFolderInfo;
|
||||
|
||||
if (root != null)
|
||||
{
|
||||
return root.GetChild(k_NewVariantBaseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
//we got here because the converted bundle was empty.
|
||||
BundleVariantFolderInfo vfolder = new BundleVariantFolderInfo(bundle.m_Name.bundleName, bundle.parent);
|
||||
BundleVariantDataInfo vdata = new BundleVariantDataInfo(bundle.m_Name.bundleName + "." + k_NewVariantBaseName, vfolder);
|
||||
bundle.parent.AddChild(vfolder);
|
||||
vfolder.AddChild(vdata);
|
||||
return vdata;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ABMoveData
|
||||
{
|
||||
internal string assetName;
|
||||
internal string bundleName;
|
||||
internal string variantName;
|
||||
|
||||
internal ABMoveData(string asset, string bundle, string variant)
|
||||
{
|
||||
assetName = asset;
|
||||
bundleName = bundle;
|
||||
variantName = variant;
|
||||
}
|
||||
|
||||
internal void Apply()
|
||||
{
|
||||
if (!DataSource.IsReadOnly()) DataSource.SetAssetBundleNameAndVariant(assetName, bundleName, variantName);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(AssetInfo asset, string bundleName, string variant)
|
||||
{
|
||||
s_MoveData.Add(new ABMoveData(asset.fullAssetName, bundleName, variant));
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(string assetName, string bundleName, string variant)
|
||||
{
|
||||
s_MoveData.Add(new ABMoveData(assetName, bundleName, variant));
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(IEnumerable<AssetInfo> assets, string bundleName, string variant)
|
||||
{
|
||||
foreach (AssetInfo asset in assets)
|
||||
MoveAssetToBundle(asset, bundleName, variant);
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(IEnumerable<string> assetNames, string bundleName, string variant)
|
||||
{
|
||||
foreach (string assetName in assetNames)
|
||||
MoveAssetToBundle(assetName, bundleName, variant);
|
||||
}
|
||||
|
||||
internal static void ExecuteAssetMove(bool forceAct = true)
|
||||
{
|
||||
int size = s_MoveData.Count;
|
||||
if (forceAct)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
bool autoRefresh = EditorPrefs.GetBool("kAutoRefresh");
|
||||
EditorPrefs.SetBool("kAutoRefresh", false);
|
||||
AssetDatabase.StartAssetEditing();
|
||||
EditorUtility.DisplayProgressBar("Moving assets to bundles", "", 0);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Moving assets to bundle " + s_MoveData[i].bundleName, System.IO.Path.GetFileNameWithoutExtension(s_MoveData[i].assetName), (float) i / (float) size);
|
||||
s_MoveData[i].Apply();
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
AssetDatabase.StopAssetEditing();
|
||||
EditorPrefs.SetBool("kAutoRefresh", autoRefresh);
|
||||
s_MoveData.Clear();
|
||||
}
|
||||
|
||||
if (!DataSource.IsReadOnly()) DataSource.RemoveUnusedAssetBundleNames();
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
//this version of CreateAsset is only used for dependent assets.
|
||||
internal static AssetInfo CreateAsset(string name, AssetInfo parent)
|
||||
{
|
||||
if (ValidateAsset(name))
|
||||
{
|
||||
string bundleName = GetBundleName(name);
|
||||
return CreateAsset(name, bundleName, parent);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static AssetInfo CreateAsset(string name, string bundleName)
|
||||
{
|
||||
if (ValidateAsset(name)) return CreateAsset(name, bundleName, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AssetInfo CreateAsset(string name, string bundleName, AssetInfo parent)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(bundleName))
|
||||
{
|
||||
return new AssetInfo(name, bundleName);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetInfo info = null;
|
||||
if (!s_GlobalAssetList.TryGetValue(name, out info))
|
||||
{
|
||||
info = new AssetInfo(name, string.Empty);
|
||||
s_GlobalAssetList.Add(name, info);
|
||||
}
|
||||
|
||||
info.AddParent(parent.displayName);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool ValidateAsset(string name)
|
||||
{
|
||||
if (!name.StartsWith("Assets/"))
|
||||
return false;
|
||||
string ext = System.IO.Path.GetExtension(name);
|
||||
if (ext == ".dll" || ext == ".cs" || ext == ".meta" || ext == ".js" || ext == ".boo")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static string GetBundleName(string asset)
|
||||
{
|
||||
return DataSource.GetAssetBundleName(asset);
|
||||
}
|
||||
|
||||
internal static int RegisterAsset(AssetInfo asset, string bundle)
|
||||
{
|
||||
if (s_DependencyTracker.ContainsKey(asset.fullAssetName))
|
||||
{
|
||||
s_DependencyTracker[asset.fullAssetName].Add(bundle);
|
||||
int count = s_DependencyTracker[asset.fullAssetName].Count;
|
||||
if (count > 1)
|
||||
asset.SetMessageFlag(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, true);
|
||||
return count;
|
||||
}
|
||||
|
||||
HashSet<string> bundles = new HashSet<string>();
|
||||
bundles.Add(bundle);
|
||||
s_DependencyTracker.Add(asset.fullAssetName, bundles);
|
||||
return 1;
|
||||
}
|
||||
|
||||
internal static void UnRegisterAsset(AssetInfo asset, string bundle)
|
||||
{
|
||||
if (s_DependencyTracker == null || asset == null)
|
||||
return;
|
||||
|
||||
if (s_DependencyTracker.ContainsKey(asset.fullAssetName))
|
||||
{
|
||||
s_DependencyTracker[asset.fullAssetName].Remove(bundle);
|
||||
int count = s_DependencyTracker[asset.fullAssetName].Count;
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
s_DependencyTracker.Remove(asset.fullAssetName);
|
||||
break;
|
||||
case 1:
|
||||
asset.SetMessageFlag(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> CheckDependencyTracker(AssetInfo asset)
|
||||
{
|
||||
if (s_DependencyTracker.ContainsKey(asset.fullAssetName)) return s_DependencyTracker[asset.fullAssetName];
|
||||
return new HashSet<string>();
|
||||
}
|
||||
|
||||
//TODO - switch local cache server on and utilize this method to stay up to date.
|
||||
//static List<string> m_importedAssets = new List<string>();
|
||||
//static List<string> m_deletedAssets = new List<string>();
|
||||
//static List<KeyValuePair<string, string>> m_movedAssets = new List<KeyValuePair<string, string>>();
|
||||
//class AssetBundleChangeListener : AssetPostprocessor
|
||||
//{
|
||||
// static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
// {
|
||||
// m_importedAssets.AddRange(importedAssets);
|
||||
// m_deletedAssets.AddRange(deletedAssets);
|
||||
// for (int i = 0; i < movedAssets.Length; i++)
|
||||
// m_movedAssets.Add(new KeyValuePair<string, string>(movedFromAssetPaths[i], movedAssets[i]));
|
||||
// //m_dirty = true;
|
||||
// }
|
||||
//}
|
||||
|
||||
internal static void LogError(string message)
|
||||
{
|
||||
Debug.LogError("AssetBundleBrowser: " + message);
|
||||
}
|
||||
|
||||
internal static void LogWarning(string message)
|
||||
{
|
||||
Debug.LogWarning("AssetBundleBrowser: " + message);
|
||||
}
|
||||
|
||||
internal static Texture2D GetFolderIcon()
|
||||
{
|
||||
if (s_folderIcon == null)
|
||||
FindBundleIcons();
|
||||
return s_folderIcon;
|
||||
}
|
||||
|
||||
internal static Texture2D GetBundleIcon()
|
||||
{
|
||||
if (s_bundleIcon == null)
|
||||
FindBundleIcons();
|
||||
return s_bundleIcon;
|
||||
}
|
||||
|
||||
internal static Texture2D GetSceneIcon()
|
||||
{
|
||||
if (s_sceneIcon == null)
|
||||
FindBundleIcons();
|
||||
return s_sceneIcon;
|
||||
}
|
||||
|
||||
private static void FindBundleIcons()
|
||||
{
|
||||
s_folderIcon = EditorGUIUtility.FindTexture("Folder Icon");
|
||||
|
||||
string packagePath = System.IO.Path.GetFullPath("Packages/com.unity.assetbundlebrowser");
|
||||
if (System.IO.Directory.Exists(packagePath))
|
||||
{
|
||||
s_bundleIcon = (Texture2D) AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Basic.png", typeof(Texture2D));
|
||||
s_sceneIcon = (Texture2D) AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Scene.png", typeof(Texture2D));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f45129f30a7a9b4caf06fa3cbb90882
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,248 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleModel
|
||||
{
|
||||
internal sealed class AssetTreeItem : TreeViewItem
|
||||
{
|
||||
private AssetInfo m_asset;
|
||||
|
||||
internal AssetInfo asset
|
||||
{
|
||||
get { return m_asset; }
|
||||
}
|
||||
|
||||
internal AssetTreeItem() : base(-1, -1)
|
||||
{
|
||||
}
|
||||
|
||||
internal AssetTreeItem(AssetInfo a) : base(a != null ? a.fullAssetName.GetHashCode() : Random.Range(int.MinValue, int.MaxValue), 0, a != null ? a.displayName : "failed")
|
||||
{
|
||||
m_asset = a;
|
||||
if (a != null)
|
||||
icon = AssetDatabase.GetCachedIcon(a.fullAssetName) as Texture2D;
|
||||
}
|
||||
|
||||
private Color m_color = new Color(0, 0, 0, 0);
|
||||
|
||||
internal Color itemColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_color.a == 0.0f && m_asset != null) m_color = m_asset.GetColor();
|
||||
return m_color;
|
||||
}
|
||||
set { m_color = value; }
|
||||
}
|
||||
|
||||
internal Texture2D MessageIcon()
|
||||
{
|
||||
return MessageSystem.GetIcon(HighestMessageLevel());
|
||||
}
|
||||
|
||||
internal MessageType HighestMessageLevel()
|
||||
{
|
||||
return m_asset != null ? m_asset.HighestMessageLevel() : MessageType.Error;
|
||||
}
|
||||
|
||||
internal bool ContainsChild(AssetInfo asset)
|
||||
{
|
||||
bool contains = false;
|
||||
if (children == null)
|
||||
return contains;
|
||||
|
||||
if (asset == null)
|
||||
return false;
|
||||
foreach (TreeViewItem child in children)
|
||||
{
|
||||
AssetTreeItem c = child as AssetTreeItem;
|
||||
if (c != null && c.asset != null && c.asset.fullAssetName == asset.fullAssetName)
|
||||
{
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssetInfo
|
||||
{
|
||||
internal bool isScene { get; set; }
|
||||
internal bool isFolder { get; set; }
|
||||
internal long fileSize;
|
||||
|
||||
private HashSet<string> m_Parents;
|
||||
private string m_AssetName;
|
||||
private string m_DisplayName;
|
||||
private string m_BundleName;
|
||||
private MessageSystem.MessageState m_AssetMessages = new MessageSystem.MessageState();
|
||||
|
||||
internal AssetInfo(string inName, string bundleName = "")
|
||||
{
|
||||
fullAssetName = inName;
|
||||
m_BundleName = bundleName;
|
||||
m_Parents = new HashSet<string>();
|
||||
isScene = false;
|
||||
isFolder = false;
|
||||
}
|
||||
|
||||
internal string fullAssetName
|
||||
{
|
||||
get { return m_AssetName; }
|
||||
set
|
||||
{
|
||||
m_AssetName = value;
|
||||
m_DisplayName = System.IO.Path.GetFileNameWithoutExtension(m_AssetName);
|
||||
|
||||
//TODO - maybe there's a way to ask the AssetDatabase for this size info.
|
||||
FileInfo fileInfo = new System.IO.FileInfo(m_AssetName);
|
||||
if (fileInfo.Exists)
|
||||
fileSize = fileInfo.Length;
|
||||
else
|
||||
fileSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal string displayName
|
||||
{
|
||||
get { return m_DisplayName; }
|
||||
}
|
||||
|
||||
internal string bundleName
|
||||
{
|
||||
get { return string.IsNullOrEmpty(m_BundleName) ? "auto" : m_BundleName; }
|
||||
}
|
||||
|
||||
internal Color GetColor()
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_BundleName))
|
||||
return Model.k_LightGrey;
|
||||
else
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
internal bool IsMessageSet(MessageSystem.MessageFlag flag)
|
||||
{
|
||||
return m_AssetMessages.IsSet(flag);
|
||||
}
|
||||
|
||||
internal void SetMessageFlag(MessageSystem.MessageFlag flag, bool on)
|
||||
{
|
||||
m_AssetMessages.SetFlag(flag, on);
|
||||
}
|
||||
|
||||
internal MessageType HighestMessageLevel()
|
||||
{
|
||||
return m_AssetMessages.HighestMessageLevel();
|
||||
}
|
||||
|
||||
internal IEnumerable<MessageSystem.Message> GetMessages()
|
||||
{
|
||||
List<MessageSystem.Message> messages = new List<MessageSystem.Message>();
|
||||
if (IsMessageSet(MessageSystem.MessageFlag.SceneBundleConflict))
|
||||
{
|
||||
string message = displayName + "\n";
|
||||
if (isScene)
|
||||
message += "Is a scene that is in a bundle with non-scene assets. Scene bundles must have only one or more scene assets.";
|
||||
else
|
||||
message += "Is included in a bundle with a scene. Scene bundles must have only one or more scene assets.";
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Error));
|
||||
}
|
||||
|
||||
if (IsMessageSet(MessageSystem.MessageFlag.DependencySceneConflict))
|
||||
{
|
||||
string message = displayName + "\n";
|
||||
message += MessageSystem.GetMessage(MessageSystem.MessageFlag.DependencySceneConflict).message;
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Error));
|
||||
}
|
||||
|
||||
if (IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))
|
||||
{
|
||||
IEnumerable<string> bundleNames = Model.CheckDependencyTracker(this);
|
||||
string message = displayName + "\n" + "Is auto-included in multiple bundles:\n";
|
||||
foreach (string bundleName in bundleNames) message += bundleName + ", ";
|
||||
message = message.Substring(0, message.Length - 2); //remove trailing comma.
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Warning));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(m_BundleName) && m_Parents.Count > 0)
|
||||
{
|
||||
//TODO - refine the parent list to only include those in the current asset list
|
||||
string message = displayName + "\n" + "Is auto included in bundle(s) due to parent(s): \n";
|
||||
foreach (string parent in m_Parents) message += parent + ", ";
|
||||
message = message.Substring(0, message.Length - 2); //remove trailing comma.
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Info));
|
||||
}
|
||||
|
||||
if (m_dependencies != null && m_dependencies.Count > 0)
|
||||
{
|
||||
string message = string.Empty;
|
||||
IOrderedEnumerable<AssetInfo> sortedDependencies = m_dependencies.OrderBy(d => d.bundleName);
|
||||
foreach (AssetInfo dependent in sortedDependencies)
|
||||
if (dependent.bundleName != bundleName)
|
||||
message += dependent.bundleName + " : " + dependent.displayName + "\n";
|
||||
if (string.IsNullOrEmpty(message) == false)
|
||||
{
|
||||
message = message.Insert(0, displayName + "\n" + "Is dependent on other bundle's asset(s) or auto included asset(s): \n");
|
||||
message = message.Substring(0, message.Length - 1); //remove trailing line break.
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Info));
|
||||
}
|
||||
}
|
||||
|
||||
messages.Add(new MessageSystem.Message(displayName + "\n" + "Path: " + fullAssetName, MessageType.Info));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
internal void AddParent(string name)
|
||||
{
|
||||
m_Parents.Add(name);
|
||||
}
|
||||
|
||||
internal void RemoveParent(string name)
|
||||
{
|
||||
m_Parents.Remove(name);
|
||||
}
|
||||
|
||||
internal string GetSizeString()
|
||||
{
|
||||
if (fileSize == 0)
|
||||
return "--";
|
||||
return EditorUtility.FormatBytes(fileSize);
|
||||
}
|
||||
|
||||
private List<AssetInfo> m_dependencies = null;
|
||||
|
||||
internal List<AssetInfo> GetDependencies()
|
||||
{
|
||||
//TODO - not sure this refreshes enough. need to build tests around that.
|
||||
if (m_dependencies == null)
|
||||
{
|
||||
m_dependencies = new List<AssetInfo>();
|
||||
if (AssetDatabase.IsValidFolder(m_AssetName))
|
||||
{
|
||||
//if we have a folder, its dependencies were already pulled in through alternate means. no need to GatherFoldersAndFiles
|
||||
//GatherFoldersAndFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string dep in AssetDatabase.GetDependencies(m_AssetName, true))
|
||||
if (dep != m_AssetName)
|
||||
{
|
||||
AssetInfo asset = Model.CreateAsset(dep, this);
|
||||
if (asset != null)
|
||||
m_dependencies.Add(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_dependencies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f5923f93f7306f4182aecb2dd06ef8d
|
||||
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: 0909f7af70db1114abcc90714f37acb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user