Files
chameleon/Assets/Scripts/Editor/FR2/FR2_Cache.cs
T
2026-07-03 10:12:02 +02:00

1880 lines
38 KiB
C#

//#define FR2_DEBUG
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace vietlabs.fr2
{
[InitializeOnLoad]
public class FR2_CacheHelper : AssetPostprocessor
{
private const int MARGIN = 5;
private const int SIZE = 5;
private static HashSet<string> scenes;
private static HashSet<string> guidsIgnore;
static FR2_CacheHelper()
{
EditorApplication.update -= InitHelper;
EditorApplication.update += InitHelper;
}
private static void InitHelper()
{
if (EditorApplication.isCompiling)
{
return;
}
// if (EditorApplication.isPlayingOrWillChangePlaymode) return;
if (!FR2_Cache.isReady)
{
return;
}
if (!FR2_Cache.Api.disabled)
{
InitListScene();
InitIgnore();
#if UNITY_2018_1_OR_NEWER
EditorBuildSettings.sceneListChanged -= InitListScene;
EditorBuildSettings.sceneListChanged += InitListScene;
#endif
EditorApplication.projectWindowItemOnGUI -= OnGUIProjectItem;
EditorApplication.projectWindowItemOnGUI += OnGUIProjectItem;
FR2_Cache.onReady -= OnCacheReady;
FR2_Cache.onReady += OnCacheReady;
}
EditorApplication.update -= InitHelper;
}
// private class AssetModificationHelper: UnityEditor.AssetModificationProcessor
// {
// static void OnWillCreateAsset(string assetName)
// {
// FR2_Cache.Api.makeDirty();
// }
// static AssetDeleteResult OnWillDeleteAsset(string name,RemoveAssetOptions options)
// {
// FR2_Cache.Api.makeDirty();
// return AssetDeleteResult.DidDelete;
// }
// private static AssetMoveResult OnWillMoveAsset(string sourcePath, string destinationPath)
// {
// FR2_Cache.Api.makeDirty();
// AssetMoveResult assetMoveResult = AssetMoveResult.DidMove;
// // Perform operations on the asset and set the value of 'assetMoveResult' accordingly.
// return assetMoveResult;
// }
// static string[] OnWillSaveAssets(string[] paths)
// {
// FR2_Cache.Api.makeDirty();
// return paths;
// }
// }
private static void OnCacheReady()
{
InitIgnore();
}
public static void InitIgnore()
{
guidsIgnore = new HashSet<string>();
foreach (string item in FR2_Setting.IgnoreAsset)
{
string guid = AssetDatabase.AssetPathToGUID(item);
if (guidsIgnore.Contains(guid))
{
continue;
}
guidsIgnore.Add(guid);
}
}
private static void InitListScene()
{
scenes = new HashSet<string>();
// string[] scenes = new string[sceneCount];
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
{
string sce = AssetDatabase.AssetPathToGUID(scene.path);
// Debug.Log(scene.path + " " + sce);
if (scenes.Contains(sce))
{
continue;
}
scenes.Add(sce);
}
}
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths)
{
if (EditorApplication.isUpdating || EditorApplication.isPlaying)
{
return;
}
//Debug.Log("OnPostProcessAllAssets : "+ FR2_Cache.Api.isReady + ":" + importedAssets.Length + ":" + deletedAssets.Length + ":" + movedAssets.Length + ":" + movedFromAssetPaths.Length);
if (!FR2_Cache.isReady)
{
#if FR2_DEBUG
Debug.Log("Not ready, will refresh anyway !");
#endif
return;
}
for (var i = 0; i < importedAssets.Length; i++)
{
if (importedAssets[i] == FR2_Cache.CachePath)
{
continue;
}
string guid = AssetDatabase.AssetPathToGUID(importedAssets[i]);
if (!FR2_Asset.IsValidGUID(guid))
{
continue;
}
if (FR2_Cache.Api.AssetMap.ContainsKey(guid))
{
FR2_Cache.Api.RefreshAsset(guid, true);
#if FR2_DEBUG
Debug.Log("Changed : " + importedAssets[i]);
#endif
continue;
}
FR2_Cache.Api.AddAsset(guid);
#if FR2_DEBUG
Debug.Log("New : " + importedAssets[i]);
#endif
}
for (var i = 0; i < deletedAssets.Length; i++)
{
string guid = AssetDatabase.AssetPathToGUID(deletedAssets[i]);
FR2_Cache.Api.RemoveAsset(guid);
#if FR2_DEBUG
Debug.Log("Deleted : " + deletedAssets[i]);
#endif
}
for (var i = 0; i < movedAssets.Length; i++)
{
string guid = AssetDatabase.AssetPathToGUID(movedAssets[i]);
FR2_Asset asset = FR2_Cache.Api.Get(guid);
if (asset != null)
{
asset.LoadAssetInfo();
}
}
#if FR2_DEBUG
Debug.Log("Changes :: " + importedAssets.Length + ":" + FR2_Cache.Api.workCount);
#endif
if (FR2_Cache.Api.workCount > 0)
{
FR2_Cache.Api.Check4Usage();
}
}
private static void OnGUIProjectItem(string guid, Rect rect)
{
if (scenes.Contains(guid))
{
var r = new Rect(rect.x - MARGIN, rect.y + MARGIN, SIZE, SIZE);
if (EditorGUIUtility.isProSkin)
{
EditorGUI.DrawRect(r, new Color32(72, 150, 191, 255));
}
else
{
EditorGUI.DrawRect(r, Color.blue);
}
}
if (guidsIgnore.Contains(guid))
{
var r = new Rect(rect.x + rect.width - 8, rect.y + MARGIN, 3, 3);
EditorGUI.DrawRect(r, Color.red);
}
if (!FR2_Cache.isReady)
{
return; // not ready
}
if (!FR2_Setting.ShowReferenceCount)
{
return;
}
FR2_Cache api = FR2_Cache.Api;
if (FR2_Cache.Api.AssetMap == null)
{
FR2_Cache.Api.Check4Changes(false, false);
}
FR2_Asset item;
if (!api.AssetMap.TryGetValue(guid, out item))
{
return;
}
if (item == null || item.UsedByMap == null)
{
return;
}
if (item.UsedByMap.Count > 0)
{
var content = new GUIContent(item.UsedByMap.Count.ToString());
float w = EditorStyles.miniLabel.CalcSize(content).x;
var r = new Rect(rect.x - w, rect.y, w, 16f);
if (r.x < -4)
{
r.x = -4f;
}
GUI.Label(r, content, EditorStyles.miniLabel);
}
}
}
[Serializable]
public class FR2_Setting
{
private static FR2_Setting d;
internal static bool showSettings;
private static HashSet<string> _hashIgnore;
private static Dictionary<string, List<string>> _IgnoreFiltered;
public static Action OnIgnoreChange;
public bool alternateColor = true;
public int excludeTypes; //32-bit type Mask
public FR2_RefDrawer.Mode groupMode;
public List<string> listIgnore = new List<string>();
public bool pingRow = true;
public bool referenceCount = true;
//public bool scanScripts = false;
public Color32 rowColor = new Color32(0, 0, 0, 12);
public Color32 ScanColor = new Color32(0, 204, 102, 255);
public Color SelectedColor = new Color(0, 0f, 1f, 0.25f);
public bool showFileSize;
public bool showSelection;
public bool showUsedByClassed = true;
public FR2_RefDrawer.Sort sortMode;
public int treeIndent = 10;
/*
Doesn't have a settings option - I will include one in next update
2. Hide the reference number - Should be in the setting above so will be coming next
3. Cache file path should be configurable - coming next in the setting
4. Disable / Selectable color in alternative rows - coming next in the setting panel
5. Applied filters aren't saved - Should be fixed in next update too
6. Hide Selection part - should be com as an option so you can quickly toggle it on or off
7. Click whole line to ping - coming next by default and can adjustable in the setting panel
*/
internal static FR2_Setting s
{
get { return FR2_Cache.Api ? FR2_Cache.Api.setting : d == null ? d = new FR2_Setting() : d; }
}
public static bool ShowUsedByClassed
{
get { return s.showUsedByClassed; }
}
public static bool ShowFileSize
{
get { return s.showUsedByClassed; }
}
public static int TreeIndent
{
get { return s.treeIndent; }
set
{
if (s.treeIndent == value)
{
return;
}
s.treeIndent = value;
setDirty();
}
}
public static bool ShowReferenceCount
{
get { return s.referenceCount; }
set
{
if (s.referenceCount == value)
{
return;
}
s.referenceCount = value;
setDirty();
}
}
public static bool ShowSelection
{
get { return s.showSelection; }
set
{
if (s.showSelection == value)
{
return;
}
s.showSelection = value;
setDirty();
}
}
public static bool AlternateRowColor
{
get { return s.alternateColor; }
set
{
if (s.alternateColor == value)
{
return;
}
s.alternateColor = value;
setDirty();
}
}
public static Color32 RowColor
{
get { return s.rowColor; }
set
{
if (s.rowColor.Equals(value))
{
return;
}
s.rowColor = value;
setDirty();
}
}
public static bool PingRow
{
get { return s.pingRow; }
set
{
if (s.pingRow == value)
{
return;
}
s.pingRow = value;
setDirty();
}
}
public static HashSet<string> IgnoreAsset
{
get
{
if (_hashIgnore == null)
{
_hashIgnore = new HashSet<string>();
if (s == null || s.listIgnore == null)
{
return _hashIgnore;
}
for (var i = 0; i < s.listIgnore.Count; i++)
{
if (_hashIgnore.Contains(s.listIgnore[i]))
{
continue;
}
_hashIgnore.Add(s.listIgnore[i]);
}
}
return _hashIgnore;
}
}
public static Dictionary<string, List<string>> IgnoreFiltered
{
get
{
if (_IgnoreFiltered == null)
{
initIgnoreFiltered();
}
return _IgnoreFiltered;
}
}
//static public bool ScanScripts
//{
// get { return s.scanScripts; }
// set {
// if (s.scanScripts == value) return;
// s.scanScripts = value; setDirty();
// }
//}
public static FR2_RefDrawer.Mode GroupMode
{
get { return s.groupMode; }
set
{
if (s.groupMode.Equals(value))
{
return;
}
s.groupMode = value;
setDirty();
}
}
public static FR2_RefDrawer.Sort SortMode
{
get { return s.sortMode; }
set
{
if (s.sortMode.Equals(value))
{
return;
}
s.sortMode = value;
setDirty();
}
}
public static bool HasTypeExcluded
{
get { return s.excludeTypes != 0; }
}
private static void setDirty()
{
if (FR2_Cache.Api != null)
{
EditorUtility.SetDirty(FR2_Cache.Api);
}
}
private static void initIgnoreFiltered()
{
FR2_Asset.ignoreTS = Time.realtimeSinceStartup;
_IgnoreFiltered = new Dictionary<string, List<string>>();
var lst = new List<string>(s.listIgnore);
lst = lst.OrderBy(x => x.Length).ToList();
int count = lst.Count;
for (var i = 0; i < count; i++)
{
string str = lst[i];
_IgnoreFiltered.Add(str, new List<string> {str});
for (int j = count - 1; j > i; j--)
{
if (lst[j].StartsWith(str))
{
_IgnoreFiltered[str].Add(lst[j]);
lst.RemoveAt(j);
count--;
}
}
}
}
public static void AddIgnore(string path)
{
if (string.IsNullOrEmpty(path) || IgnoreAsset.Contains(path) || path == "Assets")
{
return;
}
s.listIgnore.Add(path);
_hashIgnore.Add(path);
AssetType.SetDirtyIgnore();
FR2_CacheHelper.InitIgnore();
initIgnoreFiltered();
FR2_Asset.ignoreTS = Time.realtimeSinceStartup;
if (OnIgnoreChange != null)
{
OnIgnoreChange();
}
}
public static void RemoveIgnore(string path)
{
if (!IgnoreAsset.Contains(path))
{
return;
}
_hashIgnore.Remove(path);
s.listIgnore.Remove(path);
AssetType.SetDirtyIgnore();
FR2_CacheHelper.InitIgnore();
initIgnoreFiltered();
FR2_Asset.ignoreTS = Time.realtimeSinceStartup;
if (OnIgnoreChange != null)
{
OnIgnoreChange();
}
}
public static bool IsTypeExcluded(int type)
{
return (s.excludeTypes >> type & 1) != 0;
}
public static void ToggleTypeExclude(int type)
{
bool v = (s.excludeTypes >> type & 1) != 0;
if (v)
{
s.excludeTypes &= ~(1 << type);
}
else
{
s.excludeTypes |= 1 << type;
}
setDirty();
}
public static int GetExcludeType()
{
return s.excludeTypes;
}
public static bool IsIncludeAllType()
{
// Debug.Log ((AssetType.FILTERS.Length & s.excludeTypes) + " " + Mathf.Pow(2, AssetType.FILTERS.Length) );
return s.excludeTypes == 0 || Mathf.Abs(s.excludeTypes) == Mathf.Pow(2, AssetType.FILTERS.Length);
}
public static void ExcludeAllType()
{
s.excludeTypes = -1;
}
public static void IncludeAllType()
{
s.excludeTypes = 0;
}
public void DrawSettings()
{
if (FR2_Unity.DrawToggle(ref pingRow, "Full Row click to Ping"))
{
setDirty();
}
GUILayout.BeginHorizontal();
{
if (FR2_Unity.DrawToggle(ref alternateColor, "Alternate Odd & Even Row Color"))
{
setDirty();
FR2_Unity.RepaintFR2Windows();
}
EditorGUI.BeginDisabledGroup(!alternateColor);
{
Color c = EditorGUILayout.ColorField(rowColor);
if (!c.Equals(rowColor))
{
rowColor = c;
setDirty();
FR2_Unity.RepaintFR2Windows();
}
}
EditorGUI.EndDisabledGroup();
}
GUILayout.EndHorizontal();
if (FR2_Unity.DrawToggle(ref referenceCount, "Show Usage Count in Project panel"))
{
setDirty();
FR2_Unity.RepaintProjectWindows();
}
if (FR2_Unity.DrawToggle(ref showSelection, "Show Selection"))
{
setDirty();
FR2_Unity.RepaintFR2Windows();
}
if (FR2_Unity.DrawToggle(ref showUsedByClassed, "Show Asset Type in use"))
{
setDirty();
FR2_Unity.RepaintFR2Windows();
}
GUILayout.BeginHorizontal();
{
Color c = EditorGUILayout.ColorField("Duplicate Scan Color", ScanColor);
if (!c.Equals(ScanColor))
{
ScanColor = c;
setDirty();
FR2_Unity.RepaintFR2Windows();
}
}
GUILayout.EndHorizontal();
}
}
public class FR2_Cache : ScriptableObject
{
internal const string CACHE_VERSION = "2.0";
internal const int FORCE_REFRESH_DURATION = 60 * 60 * 24; // force refresh once per day
internal const string DEFAULT_CACHE_PATH = "Assets/FR2_Cache.asset";
internal static int cacheStamp;
internal static Action onReady;
internal static bool _triedToLoadCache;
internal static FR2_Cache _cache;
internal static string _cacheGUID;
internal static string _cachePath;
internal static string[] ASSET_ROOT_ALLOW =
{
"Assets/",
"ProjectSettings/"
};
[SerializeField] private bool _autoRefresh;
[SerializeField] private string _curCacheVersion;
[SerializeField] private bool _disabled;
[SerializeField] public List<FR2_Asset> AssetList;
[NonSerialized] internal Dictionary<string, FR2_Asset> AssetMap;
private int frameSkipped;
public int priority = 3;
[NonSerialized] internal List<FR2_Asset> queueLoadContent;
[NonSerialized] internal List<FR2_Asset> queueUsedBy;
internal bool ready;
[NonSerialized] internal Dictionary<string, List<FR2_Asset>> ScriptMap;
[SerializeField] internal FR2_Setting setting;
// ----------------------------------- INSTANCE -------------------------------------
[SerializeField] public int timeStamp;
[NonSerialized] internal int workCount;
internal static string CacheGUID
{
get
{
if (!string.IsNullOrEmpty(_cacheGUID))
{
return _cacheGUID;
}
if (_cache != null)
{
_cachePath = AssetDatabase.GetAssetPath(_cache);
_cacheGUID = AssetDatabase.AssetPathToGUID(_cachePath);
return _cacheGUID;
}
return null;
}
}
internal static string CachePath
{
get
{
if (!string.IsNullOrEmpty(_cachePath))
{
return _cachePath;
}
if (_cache != null)
{
_cachePath = AssetDatabase.GetAssetPath(_cache);
return _cachePath;
}
return null;
}
}
public bool Dirty { get; private set; }
internal static FR2_Cache Api
{
get
{
if (_cache != null)
{
return _cache;
}
if (!_triedToLoadCache)
{
TryLoadCache();
}
return _cache;
}
}
internal bool autoRefresh
{
get { return _autoRefresh; }
set
{
if (_autoRefresh == value)
{
return;
}
_autoRefresh = value;
if (_autoRefresh)
{
Debug.LogWarning("Auto refresh is ON !");
Check4Changes(!EditorApplication.isPlaying, true);
}
else
{
Debug.LogWarning("Auto refresh is OFF !");
}
}
}
internal bool disabled
{
get { return _disabled; }
set
{
if (_disabled == value)
{
return;
}
_disabled = value;
if (_disabled)
{
//Debug.LogWarning("FR2 is disabled - Stopping all works !");
ready = false;
EditorApplication.update -= AsyncProcess;
}
else
{
Check4Changes(!EditorApplication.isPlaying, true);
}
}
}
internal static bool isReady
{
get
{
if (!_triedToLoadCache)
{
TryLoadCache();
}
return _cache != null && _cache.ready;
}
}
internal static bool hasCache
{
get
{
if (!_triedToLoadCache)
{
TryLoadCache();
}
return _cache != null;
}
}
internal float progress
{
get
{
int n = workCount - queueLoadContent.Count - queueUsedBy.Count;
return workCount == 0 ? 1 : n / (float) workCount;
}
}
public static bool CheckSameVersion()
{
// Debug.Log((_cache == null) + " " + _cache._curCacheVersion );
if (_cache == null)
{
return false;
}
return _cache._curCacheVersion == CACHE_VERSION;
}
public void makeDirty()
{
Dirty = true;
}
private static void FoundCache(bool savePrefs, bool writeFile)
{
_cachePath = AssetDatabase.GetAssetPath(_cache);
/*
if(_cache._curCacheVersion != CACHE_VERSION)
{
EditorGUILayout.HelpBox("Incompatible cache version found, need a full refresh may take time!", UnityEditor.MessageType.Warning);
#if FR2_DEBUG
Debug.Log("Different cache version ["+ _cache._curCacheVersion + "] :: <"+ CACHE_VERSION + ">");
#endif
try{
if(!string.IsNullOrEmpty(_cachePath))
{
AssetDatabase.DeleteAsset(_cachePath);
}
}
catch {}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return;
}
*/
int elapseTime = FR2_Unity.Epoch(DateTime.Now) - _cache.timeStamp;
_cache.Check4Changes(!EditorApplication.isPlaying, elapseTime > FORCE_REFRESH_DURATION);
_cacheGUID = AssetDatabase.AssetPathToGUID(_cachePath);
if (savePrefs)
{
EditorPrefs.SetString("fr2_cache.guid", _cacheGUID);
}
if (writeFile)
{
File.WriteAllText("Library/fr2_cache.guid", _cacheGUID);
}
}
private static bool RestoreCacheFromGUID(string guid, bool savePrefs, bool writeFile)
{
if (string.IsNullOrEmpty(guid))
{
return false;
}
string path = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(path))
{
return false;
}
return RestoreCacheFromPath(path, savePrefs, writeFile);
}
private static bool RestoreCacheFromPath(string path, bool savePrefs, bool writeFile)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
_cache = FR2_Unity.LoadAssetAtPath<FR2_Cache>(path);
if (_cache != null)
{
FoundCache(savePrefs, writeFile);
}
return _cache != null;
}
private static void TryLoadCache()
{
_triedToLoadCache = true;
if (RestoreCacheFromPath(DEFAULT_CACHE_PATH, false, false))
{
return;
}
// Check EditorPrefs
string pref = EditorPrefs.GetString("fr2_cache.guid", string.Empty);
if (RestoreCacheFromGUID(pref, false, false))
{
return;
}
// Read GUID from File
if (File.Exists("Library/fr2_cache.guid"))
{
if (RestoreCacheFromGUID(File.ReadAllText("Library/fr2_cache.guid"), true, false))
{
return;
}
}
// Search whole project
string[] allAssets = AssetDatabase.GetAllAssetPaths();
for (var i = 0; i < allAssets.Length; i++)
{
if (allAssets[i].EndsWith("/FR2_Cache.asset", StringComparison.Ordinal))
{
RestoreCacheFromPath(allAssets[i], true, true);
break;
}
}
}
internal static void DeleteCache()
{
if (_cache == null)
{
return;
}
try
{
if (!string.IsNullOrEmpty(_cachePath))
{
AssetDatabase.DeleteAsset(_cachePath);
}
}
catch { }
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
internal static void CreateCache()
{
_cache = CreateInstance<FR2_Cache>();
_cache._curCacheVersion = CACHE_VERSION;
string path = Application.dataPath + DEFAULT_CACHE_PATH
.Substring(0, DEFAULT_CACHE_PATH.LastIndexOf('/') + 1).Replace("Assets", string.Empty);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
AssetDatabase.CreateAsset(_cache, DEFAULT_CACHE_PATH);
EditorUtility.SetDirty(_cache);
AssetDatabase.SaveAssets();
FoundCache(true, true);
_cache.Check4Changes(!EditorApplication.isPlaying, true);
}
internal static List<string> FindUsage(string[] listGUIDs)
{
if (!isReady)
{
return null;
}
List<FR2_Asset> refs = Api.FindAssets(listGUIDs, true);
for (var i = 0; i < refs.Count; i++)
{
List<FR2_Asset> tmp = FR2_Asset.FindUsage(refs[i]);
for (var j = 0; j < tmp.Count; j++)
{
FR2_Asset itm = tmp[j];
if (refs.Contains(itm))
{
continue;
}
refs.Add(itm);
}
}
return refs.Select(item => item.guid).ToList();
}
private void OnEnable()
{
#if FR2_DEBUG
Debug.Log("OnEnabled : " + _cache);
#endif
if (_cache == null)
{
_cache = this;
}
Check4Changes(!EditorApplication.isPlayingOrWillChangePlaymode, false);
}
internal void Clear()
{
if (AssetList != null)
{
AssetList.Clear();
}
if (queueLoadContent != null)
{
queueLoadContent.Clear();
}
if (queueUsedBy != null)
{
queueUsedBy.Clear();
}
if (AssetMap != null)
{
AssetMap.Clear();
}
Check4Changes(!EditorApplication.isPlaying, true);
}
internal void AddSymbol(string symbol, FR2_Asset asset)
{
List<FR2_Asset> list;
if (ScriptMap == null)
{
return;
}
string[] arr = symbol.Split('.');
string id = arr[arr.Length - 1];
if (!ScriptMap.TryGetValue(id, out list))
{
list = new List<FR2_Asset>();
ScriptMap.Add(id, list);
}
else if (list.Contains(asset))
{
return;
}
list.Add(asset);
}
//internal void AddSymbols(FR2_Asset asset)
//{
// for (var j = 0; j < asset.ScriptSymbols.Count; j++)
// {
// AddSymbol(asset.ScriptSymbols[j], asset);
// }
//}
//internal FR2_Asset FindSymbol(string symbol)
//{
// List<FR2_Asset> list;
// if (!ScriptMap.TryGetValue(symbol, out list)) return null;
// return list[0];
//}
//internal List<FR2_Asset> FindAllSymbol(string symbol)
//{
// List<FR2_Asset> list;
// if (!ScriptMap.TryGetValue(symbol, out list)) return null;
// return list;
//}
internal void ReadFromCache()
{
if (AssetList == null)
{
AssetList = new List<FR2_Asset>();
}
if (queueLoadContent == null)
{
queueLoadContent = new List<FR2_Asset>();
}
if (queueUsedBy == null)
{
queueUsedBy = new List<FR2_Asset>();
}
if (AssetMap == null)
{
AssetMap = new Dictionary<string, FR2_Asset>(_cache.AssetList.Count);
}
if (ScriptMap == null)
{
ScriptMap = new Dictionary<string, List<FR2_Asset>>();
}
queueLoadContent.Clear();
queueUsedBy.Clear();
ScriptMap.Clear();
AssetMap.Clear();
for (var i = 0; i < _cache.AssetList.Count; i++)
{
FR2_Asset item = _cache.AssetList[i];
//item.LoadAssetInfo();
if (AssetMap.ContainsKey(item.guid))
{
Debug.LogWarning("Something wrong, cache found twice <" + item.guid + ">");
continue;
}
AssetMap.Add(item.guid, item);
}
}
internal void ReadFromProject(bool force)
{
List<string> paths = AssetDatabase.GetAllAssetPaths().ToList();
paths.RemoveAll(item => !ASSET_ROOT_ALLOW.Any(x => item.StartsWith(x)));
string[] guids = paths.Select(item => AssetDatabase.AssetPathToGUID(item)).ToArray();
// paths.ForEach(x=> Debug.Log(x));
cacheStamp++;
// Check for new assets
for (var i = 0; i < guids.Length; i++)
{
if (!FR2_Asset.IsValidGUID(guids[i]))
{
continue;
}
FR2_Asset asset;
if (AssetMap.TryGetValue(guids[i], out asset))
{
asset.m_cacheStamp = cacheStamp;
continue;
}
;
// New asset
AddAsset(guids[i]);
}
// Check for deleted assets
for (int i = AssetList.Count - 1; i >= 0; i--)
{
if (AssetList[i].cacheStamp != cacheStamp)
{
RemoveAsset(AssetList[i]);
}
}
// Refresh definition list
//for (var i = 0; i < AssetList.Count; i++)
//{
// AddSymbols(AssetList[i]);
//}
if (force)
{
timeStamp = FR2_Unity.Epoch(DateTime.Now);
workCount += AssetMap.Count;
queueLoadContent.AddRange(AssetMap.Values.ToList());
}
}
internal void Check4Changes(bool checkProject, bool checkContent)
{
#if FR2_DEBUG
Debug.Log(string.Format("Check4Changes : checkProject={0} checkContent={1}", checkProject, checkContent));
#endif
// if(checkContent)
// {
// if(_cache.AssetList!= null) _cache.AssetList.Clear();
// if(_cache.AssetMap!= null) _cache.AssetMap.Clear();
// }
//if (EditorApplication.isCompiling || EditorApplication.isPlayingOrWillChangePlaymode) return;
ready = false;
ReadFromCache();
if (checkProject)
{
ReadFromProject(checkContent);
}
Check4Usage();
#if FR2_DEBUG
Debug.Log("After checking :: WorkCount :: " + workCount);
#endif
}
internal void RefreshAsset(string guid, bool force)
{
if (!AssetMap.ContainsKey(guid))
{
return;
}
RefreshAsset(AssetMap[guid], force);
}
internal void RefreshSelection()
{
string[] list = FR2_Unity.Selection_AssetGUIDs;
for (var i = 0; i < list.Length; i++)
{
RefreshAsset(list[i], true);
}
Check4Work();
}
internal void RefreshAsset(FR2_Asset asset, bool force)
{
#if FR2_DEBUG
Debug.Log("RefreshAsset: " + asset.guid + ":" + workCount);
#endif
workCount++;
if (force)
{
asset.MarkAsDirty();
if (asset.type == FR2_AssetType.FOLDER && !asset.IsMissing)
{
string[] dirs = Directory.GetDirectories(asset.assetPath, "*", SearchOption.AllDirectories);
//refresh children directories as well
for (var i = 0; i < dirs.Length; i++)
{
string guid = AssetDatabase.AssetPathToGUID(dirs[i]);
FR2_Asset child = Api.Get(guid);
if (child == null)
{
continue;
}
workCount++;
child.MarkAsDirty();
queueLoadContent.Add(child);
}
}
}
queueLoadContent.Add(asset);
}
internal void AddAsset(string guid)
{
if (AssetMap.ContainsKey(guid))
{
Debug.LogWarning("guid already exist <" + guid + ">");
return;
}
FR2_Asset first = AssetList.FirstOrDefault(item => item.guid == guid);
if (first != null)
{
Debug.LogWarning("Should catch asset import ! " + first.assetPath);
first.LoadAssetInfo();
RefreshAsset(first, true);
return;
}
var asset = new FR2_Asset(guid);
asset.m_cacheStamp = cacheStamp;
AssetList.Add(asset);
AssetMap.Add(guid, asset);
// Do not load content for FR2_Cache asset
if (guid == CacheGUID)
{
return;
}
workCount++;
queueLoadContent.Add(asset);
}
internal void RemoveAsset(string guid)
{
if (!AssetMap.ContainsKey(guid))
{
return;
}
RemoveAsset(AssetMap[guid]);
}
internal void RemoveAsset(FR2_Asset asset)
{
AssetList.Remove(asset);
// Deleted Asset : still in the map but not in the AssetList
asset.state = FR2_AssetState.MISSING;
//for (var i = 0; i < asset.ScriptSymbols.Count; i++)
//{
// var s = asset.ScriptSymbols[i];
// var l = FindAllSymbol(s);
// if (l != null && l.Contains(asset)) l.Remove(asset);
//}
}
internal void Check4Usage()
{
queueUsedBy.Clear();
queueUsedBy.AddRange(AssetMap.Values.ToArray());
workCount += queueUsedBy.Count;
for (var i = 0; i < queueUsedBy.Count; i++)
{
FR2_Asset q = queueUsedBy[i];
if (q.UsedByMap == null)
{
q.UsedByMap = new Dictionary<string, FR2_Asset>();
q.ScriptUsage = new List<string>();
}
else
{
q.UsedByMap.Clear();
q.ScriptUsage.Clear();
}
}
ready = workCount > 0;
Check4Work();
}
internal void Check4Work()
{
if (disabled || workCount == 0)
{
return;
}
ready = false;
EditorApplication.update -= AsyncProcess;
EditorApplication.update += AsyncProcess;
}
//public float asyncDelay = 0.5f;
//public const float FRAME_DURATION = 1/5000f;
internal void AsyncProcess()
{
if (this == null)
{
return;
}
//EditorApplication.isPlayingOrWillChangePlaymode ||
if (EditorApplication.isCompiling || EditorApplication.isUpdating)
{
return;
}
//Debug.Log("Async process :: ");
if (frameSkipped++ < 10 - 2 * priority)
{
return;
}
frameSkipped = 0;
float t = Time.realtimeSinceStartup;
#if FR2_DEBUG
//Debug.Log(Mathf.Round(t) + " : " + progress*workCount + "/" + workCount + ":" + isReady);
#endif
if (!AsyncWork(queueLoadContent, AsyncLoadContent, t))
{
return;
}
if (!AsyncWork(queueUsedBy, AsyncUsedBy, t))
{
return;
}
workCount = 0;
ready = true;
EditorApplication.update -= AsyncProcess;
EditorUtility.SetDirty(this);
//AssetDatabase.SaveAssets(); //DO NOT SAVE ASSET !
//var windows = Resources.FindObjectsOfTypeAll<EditorWindow>();
//for (var i = 0; i < windows.Length; i++)
//{
// windows[i].Repaint();
//}
//Refresh selection
if (onReady != null)
{
onReady();
}
}
internal bool AsyncWork<T>(List<T> arr, Action<int, T> action, float t)
{
float FRAME_DURATION = 1 / 1000f * (priority * 5 + 1); //prevent zero
int c = arr.Count;
var counter = 0;
while (c-- > 0)
{
T last = arr[c];
arr.RemoveAt(c);
action(c, last);
//workCount--;
float dt = Time.realtimeSinceStartup - t - FRAME_DURATION;
if (dt >= 0)
{
return false;
}
counter++;
}
return true;
}
internal void AsyncLoadContent(int idx, FR2_Asset asset)
{
asset.LoadContent(false);
//Debug.Log("AsyncLoadContent:" + idx + ":" + asset.guid + ":" + asset.UseGUIDs.Count);
//if (asset.ScriptSymbols.Count > 0)
//{
// AddSymbols(asset);
// //if (asset.assetName == "Script1.cs") Debug.Log(asset.assetName + " ---> " + asset.ScriptTokens.Count + ":" + asset.ScriptSymbols.Count);
//}
}
internal void AsyncUsedBy(int idx, FR2_Asset asset)
{
if (AssetMap == null)
{
Check4Changes(!EditorApplication.isPlaying, false);
}
if (asset.IsFolder)
{
return;
}
//for (var i = 0; i < asset.ScriptTokens.Count; i++)
//{
// var token = asset.ScriptTokens[i];
// var all = FindAllSymbol(token);
// if (all == null) continue;
// //Debug.Log(asset.assetName + " :: Find <" + token + "> ---> " + all.Count);
// asset.ScriptUsage.Add(token); //definition found !
// for (var j = 0; j < all.Count; j++)
// {
// // add to usage list
// if (all[j].IsMissing || all[j].UsedByMap == null) continue;
// if (!all[j].UsedByMap.ContainsKey(asset.guid))
// {
// all[j].AddUsedBy(asset.guid, asset);
// }
// }
//}
foreach (KeyValuePair<string, HashSet<int>> item in asset.UseGUIDs)
{
FR2_Asset tAsset;
if (AssetMap.TryGetValue(item.Key, out tAsset))
{
if (tAsset == null || tAsset.UsedByMap == null)
{
continue;
}
if (!tAsset.UsedByMap.ContainsKey(asset.guid))
{
tAsset.AddUsedBy(asset.guid, asset);
}
}
}
// for (var i = 0; i < asset.UseGUIDs.Count; i++)
// {
// FR2_Asset tAsset;
// if (AssetMap.TryGetValue(asset.UseGUIDs[i], out tAsset))
// {
// if (tAsset == null || tAsset.UsedByMap == null) continue;
// if (!tAsset.UsedByMap.ContainsKey(asset.guid))
// {
// tAsset.AddUsed(asset.guid, asset);
// }
// }
// }
}
//---------------------------- Dependencies -----------------------------
internal FR2_Asset Get(string guid, bool isForce = false)
{
// string path = AssetDatabase.GUIDToAssetPath(guid);
// if (!isForce && !string.IsNullOrEmpty(path))
// {
// foreach (var item in FR2_Setting.IgnoreFiltered.Keys)
// {
// if (path.StartsWith(item))
// {
// FR2_Window.isNoticeIgnore = true;
// return null;
// }
// }
// }
return AssetMap.ContainsKey(guid) ? AssetMap[guid] : null;
}
internal List<FR2_Asset> FindAssetsOfType(FR2_AssetType type)
{
var result = new List<FR2_Asset>();
foreach (KeyValuePair<string, FR2_Asset> item in AssetMap)
{
if (item.Value.type != type)
{
continue;
}
result.Add(item.Value);
}
return result;
}
internal List<FR2_Asset> FindAssets(string[] guids, bool scanFolder)
{
if (AssetMap == null)
{
Check4Changes(!EditorApplication.isPlaying, false);
}
var result = new List<FR2_Asset>();
if (!isReady)
{
#if FR2_DEBUG
Debug.LogWarning("Cache not ready !");
#endif
return result;
}
var folderList = new List<FR2_Asset>();
if (guids.Length == 0)
{
return result;
}
for (var i = 0; i < guids.Length; i++)
{
string guid = guids[i];
FR2_Asset asset;
if (!AssetMap.TryGetValue(guid, out asset))
{
continue;
}
if (asset.IsMissing)
{
continue;
}
if (asset.IsFolder)
{
if (!folderList.Contains(asset))
{
folderList.Add(asset);
}
}
else
{
result.Add(asset);
}
}
if (!scanFolder || folderList.Count == 0)
{
return result;
}
int count = folderList.Count;
for (var i = 0; i < count; i++)
{
FR2_Asset item = folderList[i];
// for (var j = 0; j < item.UseGUIDs.Count; j++)
// {
// FR2_Asset a;
// if (!AssetMap.TryGetValue(item.UseGUIDs[j], out a)) continue;
foreach (KeyValuePair<string, HashSet<int>> useM in item.UseGUIDs)
{
FR2_Asset a;
if (!AssetMap.TryGetValue(useM.Key, out a))
{
continue;
}
if (a.IsMissing)
{
continue;
}
if (a.IsFolder)
{
if (!folderList.Contains(a))
{
folderList.Add(a);
count++;
}
}
else
{
result.Add(a);
}
}
}
return result;
}
//---------------------------- Dependencies -----------------------------
internal List<List<string>> ScanSimilar(Action IgnoreWhenScan, Action IgnoreFolderWhenScan)
{
if (AssetMap == null)
{
Check4Changes(!EditorApplication.isPlaying, true);
}
var dict = new Dictionary<string, List<FR2_Asset>>();
foreach (KeyValuePair<string, FR2_Asset> item in AssetMap)
{
if (item.Value == null)
{
continue;
}
if (item.Value.IsMissing || item.Value.IsFolder)
{
continue;
}
if (item.Value.inPlugins)
{
continue;
}
if (item.Value.inEditor)
{
continue;
}
// if (item.Value.extension != ".png" && item.Value.extension != ".jpg") continue;
if (FR2_Setting.IsTypeExcluded(AssetType.GetIndex(item.Value.extension)))
{
// Debug.LogWarning("ignore: " +item.Value.assetPath);
if (IgnoreWhenScan != null)
{
IgnoreWhenScan();
}
continue;
}
var isBreak = false;
foreach (string ignore in FR2_Setting.IgnoreFiltered.Keys)
{
if (item.Value.assetPath.StartsWith(ignore))
{
isBreak = true;
if (IgnoreFolderWhenScan != null)
{
IgnoreFolderWhenScan();
}
// Debug.Log("ignore " + item.Value.assetPath + " path ignore " + ignore);
break;
}
}
if (isBreak)
{
continue;
}
string hash = item.Value.fileInfoHash;
if (string.IsNullOrEmpty(hash))
{
#if FR2_DEBUG
Debug.LogWarning("Hash can not be null! ");
#endif
continue;
}
List<FR2_Asset> list;
if (!dict.TryGetValue(hash, out list))
{
list = new List<FR2_Asset>();
dict.Add(hash, list);
}
list.Add(item.Value);
}
List<List<FR2_Asset>> result = dict.Values.Where(item => item.Count > 1).ToList();
result.Sort((item1, item2) => { return item2[0].fileSize.CompareTo(item1[0].fileSize); });
return result.Select(l => l.Select(i => i.assetPath).ToList()).ToList();
}
//internal List<FR2_DuplicateInfo> ScanDuplication(){
// if (AssetMap == null) Check4Changes(false);
// var dict = new Dictionary<string, FR2_DuplicateInfo>();
// foreach (var item in AssetMap){
// if (item.Value.IsMissing || item.Value.IsFolder) continue;
// var hash = item.Value.GetFileInfoHash();
// FR2_DuplicateInfo info;
// if (!dict.TryGetValue(hash, out info)){
// info = new FR2_DuplicateInfo(hash, item.Value.fileSize);
// dict.Add(hash, info);
// }
// info.assets.Add(item.Value);
// }
// var result = new List<FR2_DuplicateInfo>();
// foreach (var item in dict){
// if (item.Value.assets.Count > 1){
// result.Add(item.Value);
// }
// }
// result.Sort((item1, item2)=>{
// return item2.fileSize.CompareTo(item1.fileSize);
// });
// return result;
//}
internal List<FR2_Asset> ScanUnused()
{
if (AssetMap == null)
{
Check4Changes(!EditorApplication.isPlaying, false);
}
var result = new List<FR2_Asset>();
foreach (KeyValuePair<string, FR2_Asset> item in AssetMap)
{
FR2_Asset v = item.Value;
if (v.IsMissing || v.inEditor || v.IsScript || v.inResources || v.inPlugins || v.inStreamingAsset ||
v.IsFolder)
{
continue;
}
if (v.UsedByMap.Count == 0 && !FR2_Asset.IGNORE_UNUSED_GUIDS.Contains(v.guid))
{
result.Add(v);
}
}
result.Sort((item1, item2) =>
{
if (item1.extension == item2.extension)
{
return item1.assetPath.CompareTo(item2.assetPath);
}
return item1.extension.CompareTo(item2.extension);
});
return result;
}
}
}