Ejemplo n.º 1
0
        /// <summary>
        /// Does list already have an entry similar to the one supplied?
        /// Similar means same AssetPath, same IndentLevel,
        /// same asset that has higher indent level above it (parent).
        /// </summary>
        /// <param name="list"></param>
        /// <param name="check"></param>
        /// <param name="assetParentOfCheck"></param>
        /// <returns></returns>
        static bool Contains(List <AssetUserFlattened> list, AssetUserFlattened check, string assetParentOfCheck)
        {
            for (int n = list.Count - 1; n >= 0; --n)
            {
                if (list[n].IndentLevel < check.IndentLevel)
                {
                    // we've gone past the indent level we need to check for
                    break;
                }

                if (list[n].AssetPath.Equals(check.AssetPath, StringComparison.OrdinalIgnoreCase) &&
                    list[n].IndentLevel == check.IndentLevel &&
                    GetAssetUsageParent(list, n) == assetParentOfCheck)
                {
                    return(true);
                }
            }

            return(false);
        }
Ejemplo n.º 2
0
        // ==================================================================================

        static void Create(AssetDependencies data, Queue <string> openSet, bool debugLog = false,
                           bool recursiveGetDependencies = false)
        {
            var assetDependencies = data.GetAssetDependencies();

            assetDependencies.Clear();

            // -------------------------------------------------------------

            // assets that we've finished inspecting
            var closedSet = new HashSet <string>();

            // -------------------------------------------------------------

            while (openSet.Count > 0)
            {
                var newAssetToInspect = openSet.Dequeue();

                if (string.IsNullOrEmpty(newAssetToInspect))
                {
                    continue;
                }

                closedSet.Add(newAssetToInspect);

                string[] foundDependencies = GetDependencies(newAssetToInspect, recursiveGetDependencies);

                if (foundDependencies.Length <= 0)
                {
                    // this asset doesn't use others
                    continue;
                }

                StringBuilder stringBuilder = null;
                if (debugLog)
                {
                    stringBuilder = new StringBuilder();
                    stringBuilder.Append("Assets used by ");
                    stringBuilder.Append(newAssetToInspect);
                    stringBuilder.Append(" (");
                    stringBuilder.Append(foundDependencies.Length.ToString());
                    stringBuilder.Append("):\n");
                }

                DependencyEntry assetsUsed;
                if (assetDependencies.ContainsKey(newAssetToInspect))
                {
                    assetsUsed = assetDependencies[newAssetToInspect];
                }
                else
                {
                    assetsUsed = new DependencyEntry();
                    assetDependencies.Add(newAssetToInspect, assetsUsed);
                }

                // -------------------------------------------------------------

                // we're looping through the assets that newAssetToInspect uses
                // so add them to the `DependencyEntry.Uses` list
                for (int n = 0, len = foundDependencies.Length; n < len; ++n)
                {
                    if (string.IsNullOrEmpty(foundDependencies[n]))
                    {
                        continue;
                    }

                    if (foundDependencies[n].Equals(newAssetToInspect, StringComparison.Ordinal))
                    {
                        // for some reason, the API reports assets to be using their own selves
                        // so skip it when they have the same value
                        continue;
                    }

                    if (debugLog)
                    {
                        stringBuilder.Append(n.ToString());
                        stringBuilder.Append(". ");
                        stringBuilder.Append(foundDependencies[n]);
                        stringBuilder.Append("\n");
                    }

                    // --------------------------------------------------------

                    if (!assetsUsed.Uses.Contains(foundDependencies[n]))
                    {
                        assetsUsed.Uses.Add(foundDependencies[n]);
                    }

                    // --------------------------------------------------------

                    // now record that dependency as being used by `newAssetToInspect`
                    DependencyEntry assetsUsedByEdit;
                    if (assetDependencies.ContainsKey(foundDependencies[n]))
                    {
                        assetsUsedByEdit = assetDependencies[foundDependencies[n]];
                    }
                    else
                    {
                        assetsUsedByEdit = new DependencyEntry();
                        assetDependencies.Add(foundDependencies[n], assetsUsedByEdit);
                    }

                    if (!assetsUsedByEdit.Users.Contains(newAssetToInspect))
                    {
                        assetsUsedByEdit.Users.Add(newAssetToInspect);
                    }

                    // --------------------------------------------------------

                    if (closedSet.Contains(foundDependencies[n]))
                    {
                        // this asset was already searched through. skip it.
                        continue;
                    }

                    if (!openSet.Contains(foundDependencies[n]))
                    {
                        // add to list of assets to search through
                        openSet.Enqueue(foundDependencies[n]);
                    }
                }

                if (debugLog)
                {
                    Debug.Log(stringBuilder.ToString());
                }
            }

            // ====================================================================
            // Create the AssetUserFlattened List
            // This is the recursively calculated users of the asset.
            // Indent Levels signify which asset uses which.

            List <AssetUserFlattened> openFlattenedSet = new List <AssetUserFlattened>();

            foreach (var pair in assetDependencies)
            {
                var assetUsers = pair.Value.Users;

                if (assetUsers.Count == 0)
                {
                    continue;
                }

                var usersFlattened = new List <AssetUserFlattened>(assetUsers.Count);
                pair.Value.UsersFlattened = usersFlattened;

                openFlattenedSet.Clear();

                for (int n = 0, len = assetUsers.Count; n < len; ++n)
                {
                    AssetUserFlattened newEntry = new AssetUserFlattened(assetUsers[n], 1
#if BRT_ASSET_DEPENDENCY_DEBUG
                                                                         , "[initial]"
#endif
                                                                         );

                    // put them in reverse since always take from the end of the openFlattenedSet
                    openFlattenedSet.Insert(0, newEntry);
                }

                var endlessLoopGuard = 0;
                while (openFlattenedSet.Count > 0)
                {
                    ++endlessLoopGuard;
                    if (endlessLoopGuard >= 9999)                     // that ought to be enough for real-world usage (I hope)
                    {
                        break;
                    }


                    var userAsset = openFlattenedSet[openFlattenedSet.Count - 1];
                    openFlattenedSet.RemoveAt(openFlattenedSet.Count - 1);


                    var assetUsed = GetAssetUsageParent(usersFlattened, usersFlattened.Count - 1);
                    if (Contains(usersFlattened, userAsset, assetUsed))
                    {
                        // `userAsset` is already added previously in this list in same indent level and same parent
                        // can't add this because it would be a duplicate entry

                        continue;
                    }

                    usersFlattened.Add(userAsset);

                    if (userAsset.AssetPath.Equals(pair.Key, StringComparison.OrdinalIgnoreCase))
                    {
                        // no need to go inside this further because this is
                        // already what we're primarily going inside currently
                        userAsset.CyclicDependency = true;
#if BRT_ASSET_DEPENDENCY_DEBUG
                        userAsset.DebugInfo = "[selected cyclic]";
#endif
                        usersFlattened[usersFlattened.Count - 1] = userAsset;
                        continue;
                    }

                    var    userAssetIsFbxUsingMaterial = false;
                    string materialAssignedAsDefault   = null;

                    if (usersFlattened.Count >= 2)
                    {
                        // note: usersFlattened[usersFlattened.Count - 1] is userAsset
                        //

                        var noNeedToAddUsersOfUser = false;

                        if (userAsset.IndentLevel > 1)
                        {
                            var currentIndentLevel = userAsset.IndentLevel - 1;

                            if (debugLog && pair.Key.Contains("Runtime-Only DLL/TextMeshPro.dll") &&
                                userAsset.AssetPath.Contains("AbilityEntry Settings.asset"))
                            {
                                Debug.LogFormat(
                                    "=========================================================\nChecking for cyclic dependencies for {0} at index {1}\nLooking for Indent Level {2}",
                                    userAsset.AssetPath, (usersFlattened.Count - 1).ToString(), currentIndentLevel);
                            }

                            // start just before our own position in the list, then go backwards
                            for (int traceN = usersFlattened.Count - 2; traceN >= 0; --traceN)
                            {
                                if (usersFlattened[traceN].IndentLevel > currentIndentLevel)
                                {
                                    // not the indent level we're looking for

                                    if (debugLog && pair.Key.Contains("Runtime-Only DLL/TextMeshPro.dll") &&
                                        userAsset.AssetPath.Contains("AbilityEntry Settings.asset"))
                                    {
                                        Debug.LogFormat("Skipped element {0} since its indent level is: {1} (looking for {2})",
                                                        traceN, usersFlattened[traceN].IndentLevel.ToString(), currentIndentLevel);
                                    }

                                    continue;
                                }

                                // at this point, the current element is above our indent level, meaning it's what the asset uses

                                if (debugLog && pair.Key.Contains("Runtime-Only DLL/TextMeshPro.dll") &&
                                    userAsset.AssetPath.Contains("AbilityEntry Settings.asset"))
                                {
                                    Debug.LogFormat("Now at element {0}: {1} at indent level {2}. Checking if same as {3}",
                                                    traceN, usersFlattened[traceN].AssetPath, usersFlattened[traceN].IndentLevel.ToString(),
                                                    userAsset.AssetPath);
                                }

                                currentIndentLevel = usersFlattened[traceN].IndentLevel - 1;

                                if (usersFlattened[traceN].AssetPath
                                    .Equals(userAsset.AssetPath, StringComparison.OrdinalIgnoreCase))
                                {
                                    // this is an asset we've been in earlier.
                                    // should not add users of this asset, because that includes the
                                    // one we've already been in, and it would be an endless recursion
                                    userAsset.CyclicDependency = true;
#if BRT_ASSET_DEPENDENCY_DEBUG
                                    userAsset.DebugInfo = "[earlier indent cyclic]";
#endif
                                    usersFlattened[usersFlattened.Count - 1] = userAsset;
                                    noNeedToAddUsersOfUser = true;


                                    if (debugLog && pair.Key.Contains("Runtime-Only DLL/TextMeshPro.dll") &&
                                        userAsset.AssetPath.Contains("AbilityEntry Settings.asset"))
                                    {
                                        Debug.LogFormat(
                                            "Element {0} is found to be same so latest entry is marked as cyclic dependency. check is done.",
                                            traceN);
                                    }

                                    break;
                                }

                                if (usersFlattened[traceN].IndentLevel <= 1)
                                {
                                    // went through the last asset in this usage chain, no need to look further

                                    if (debugLog && pair.Key.Contains("Runtime-Only DLL/TextMeshPro.dll") &&
                                        userAsset.AssetPath.Contains("AbilityEntry Settings.asset"))
                                    {
                                        Debug.LogFormat("Element {0} has indent level of {1} so aborting the check",
                                                        traceN, usersFlattened[traceN].IndentLevel.ToString());
                                    }

                                    break;
                                }
                            }

                            if (debugLog && pair.Key.Contains("Runtime-Only DLL/TextMeshPro.dll") &&
                                userAsset.AssetPath.Contains("AbilityEntry Settings.asset"))
                            {
                                Debug.LogFormat(
                                    "=========================================================\nDone checking for cyclic dependencies for {0} at index {1}",
                                    userAsset.AssetPath, (usersFlattened.Count - 1).ToString());
                            }
                        }

                        if (!noNeedToAddUsersOfUser)
                        {
                            for (int n = usersFlattened.Count - 2; n >= 0; --n)
                            {
                                if (usersFlattened[n].AssetPath == userAsset.AssetPath)
                                {
                                    // users of this user has already been shown in earlier entries
                                    // although from a different usage chain

#if BRT_ASSET_DEPENDENCY_DEBUG
                                    userAsset.DebugInfo += " [already visited]";
#endif
                                    // update the entry
                                    usersFlattened[usersFlattened.Count - 1] = userAsset;

                                    noNeedToAddUsersOfUser = true;

                                    break;
                                }
                            }
                        }

                        int idxOfPrevious;
                        if (!noNeedToAddUsersOfUser &&
                            IsFileTypeBeforeAnother(usersFlattened, usersFlattened.Count - 1,
                                                    ".mat", ".fbx", out idxOfPrevious))
                        {
                            // a Material being used by an FBX is only as a default, initial value for that FBX.
                            // after the FBX is instantiated, we have no guarantee that Material is still being
                            // used, so additional checks are needed

                            // check the assets that use the FBX file, meaning check the places where that FBX
                            // is instantiated or referenced, which are either prefabs, scenes, or scriptable objects.
                            //
                            // do those prefabs/scenes/scriptable objects really use that default material, or has it
                            // been overriden?

                            userAssetIsFbxUsingMaterial = true;

                            materialAssignedAsDefault = usersFlattened[idxOfPrevious].AssetPath;
                            var fbxUsingTheMaterialAsDefault = userAsset.AssetPath;

                            if (debugLog)
                            {
                                Debug.LogFormat("Detected fbx using a material.\n" +
                                                "Material:\n{0}\n" +
                                                "Fbx:\n{1}\n" +
                                                "Checking the assets that use the fbx if they really also use the material...",
                                                materialAssignedAsDefault, fbxUsingTheMaterialAsDefault);
                            }

                            var isMaterialReallyBeingUsed =
                                AreInstancesOfFbxUsingMaterial(fbxUsingTheMaterialAsDefault, materialAssignedAsDefault,
                                                               assetDependencies, debugLog);

                            if (!isMaterialReallyBeingUsed)
                            {
#if BRT_ASSET_DEPENDENCY_DEBUG
                                userAsset.DebugInfo += " [using as default for material]";
#endif
                                usersFlattened[usersFlattened.Count - 1] = userAsset;
                                noNeedToAddUsersOfUser = true;
                            }
                        }

                        if (!noNeedToAddUsersOfUser &&
                            userAsset.AssetPath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
                        {
                            // something being used by a .cs script file is only as a default, initial value for that script
                            // after the script is instantiated, we have no guarantee that something is still being
                            // used, so it doesn't make sense to go further
#if BRT_ASSET_DEPENDENCY_DEBUG
                            userAsset.DebugInfo += " [using as default for script]";
#endif
                            usersFlattened[usersFlattened.Count - 1] = userAsset;
                            noNeedToAddUsersOfUser = true;
                        }

                        if (noNeedToAddUsersOfUser)
                        {
                            continue;
                        }
                    }

                    if (assetDependencies.ContainsKey(userAsset.AssetPath))
                    {
                        var usersOfUser = assetDependencies[userAsset.AssetPath].Users;
                        for (int n = 0, len = usersOfUser.Count; n < len; ++n)
                        {
                            AssetUserFlattened newEntry = new AssetUserFlattened(usersOfUser[n], userAsset.IndentLevel + 1);

                            if (userAssetIsFbxUsingMaterial)
                            {
                                if (!IsInstanceOfFbxUsingMaterial(userAsset.AssetPath, usersOfUser[n],
                                                                  materialAssignedAsDefault, assetDependencies, debugLog))
                                {
                                    continue;
                                }

                                if (usersOfUser[n].EndsWith(".unity", StringComparison.OrdinalIgnoreCase))
                                {
                                    // userAsset is an fbx which uses a material
                                    // newEntry is a scene that uses (an instance of) that fbx
                                    // BUT we are not sure that
                                    newEntry.IndentLevel = userAsset.IndentLevel;

                                    if (Contains(usersFlattened, newEntry, userAsset.AssetPath))
                                    {
                                        // already added
                                        continue;
                                    }
                                }

#if BRT_ASSET_DEPENDENCY_DEBUG
                                newEntry.DebugInfo = " [uses fbx which uses material]";
#endif
                            }

                            openFlattenedSet.Add(newEntry);
                        }
                    }
                }
            }

            if (debugLog)
            {
                Debug.Log("===================================================");

                foreach (var pair in assetDependencies)
                {
                    var stringBuilder = new StringBuilder();

                    stringBuilder.Append(pair.Key);

                    stringBuilder.Append(" used by:\n");

                    for (int n = 0, len = pair.Value.Users.Count; n < len; ++n)
                    {
                        stringBuilder.Append(n.ToString());
                        stringBuilder.Append(". ");
                        stringBuilder.Append(pair.Value.Users[n]);
                        stringBuilder.Append("\n");
                    }

                    Debug.Log(stringBuilder.ToString());
                }
            }
        }