Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Is it possible to use Addressables like File and Directory from System.IO?

Discussion in 'Addressables' started by Sherwinnie, Jan 10, 2021.

  1. Sherwinnie

    Sherwinnie

    Joined:
    Jan 24, 2020
    Posts:
    19
    Sorry in advance if something like this already exists, but I have been trying to find a similar solution but can not find one that meets my need.

    Take a look of my Addressables structure that I will use as an example here:
    Screen Shot 2021-01-10 at 10.51.50 PM.png

    And all I want is some simple functions like Addressables.PathExists(string path), Addressables.DirectoryExists(string directory), Addressables.GetFilePathsFromDirectory(string directory). So if I call Addressables.DirectoryExists("TimeTraveler") or Addressables.DirectoryExists("TimeTraveler/Mellifluous"), they will return true, because they do exist as keys in the Addressable system.

    And if I call Addressables.GetFilePathsFromDirectory("TimeTraveler/Mellifluous/SongInfo/StoryBoard@hard/Images/Images"), I can get a List<string> contains "TimeTraveler/Mellifluous/SongInfo/StoryBoard@hard/Images/Images/test.png" and "TimeTraveler/Mellifluous/SongInfo/StoryBoard@hard/Images/Images/Esit.png".

    The closest thing I can think of now is that I will label everything in Addressables with some kind of "Everything" label, called LoadResourceLocations("Everything"), and then do some parsing and create a custom Manager for this purpose, but before I fall into the rabbit hole, I just want to make sure I am recreating a solution if one already exists...

    Okay, now, I totally understand the preferred workflow for Addressables is to use labels. However, my main point is that if Addressables is structured like Directory, looks like Directory, and feels like Directory, and we can load the Assets through the keys that are exactly like file path in normal OS (ex Addressables.LoadAssetAsync<TextAsset>("TimeTraveler/.../chart.txt")), then my instinct would tell me that I can use it somehow like Directory as in System.IO. But it is just not something I can find via Google nor on this forum so far....

    Again, sorry in advance if this feature or a solution already exists somewhere. Either way, please enlighten me a way out of this.

    Thank you!
     

    Attached Files:

    Last edited: Jan 10, 2021
  2. Sherwinnie

    Sherwinnie

    Joined:
    Jan 24, 2020
    Posts:
    19
    After some time, I still decide to write my own Parser for this.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.IO;
    5.  
    6.  
    7. public class GenericDirectoryPathLibrary
    8. {
    9.     internal class DirectoryInfoHolder
    10.     {
    11.         HashSet<string> ChildrenFiles = new HashSet<string>();
    12.         HashSet<string> ChildrenDirectories = new HashSet<string>();
    13.  
    14.         public void AddToChildrenDirectoies(string path)
    15.         {
    16.             if (ChildrenDirectories.Contains(path) == false)
    17.             {
    18.                 ChildrenDirectories.Add(path);
    19.             }
    20.         }
    21.  
    22.         public void AddToChildrenFiles(string path)
    23.         {
    24.             if(ChildrenFiles.Contains(path) == false)
    25.             {
    26.                 ChildrenFiles.Add(path);
    27.             }
    28.         }
    29.  
    30.         public HashSet<string> GetChildrenFiles()
    31.         {
    32.             return new HashSet<string>(ChildrenFiles);
    33.         }
    34.  
    35.         public HashSet<string> GetChildrenDirectories()
    36.         {
    37.             return new HashSet<string>(ChildrenDirectories);
    38.         }
    39.     }
    40.  
    41.     private Dictionary<string, DirectoryInfoHolder> DirectoryInfoHolderDictionary = new Dictionary<string, DirectoryInfoHolder>();
    42.     private HashSet<string> AllPaths = new HashSet<string>();
    43.     private HashSet<string> FilePaths = new HashSet<string>();
    44.  
    45.     /// <summary>
    46.     /// A Path is recognized as a "FilePath" if it contains an extension at the end. Or else it is considered to be a "DirectoryPath"
    47.     /// </summary>
    48.     /// <param name="paths"></param>
    49.     public GenericDirectoryPathLibrary(HashSet<string> paths)
    50.     {
    51.         AllPaths = new HashSet<string>(paths);
    52.         ParsePaths();
    53.     }
    54.  
    55.     public void ParsePaths()
    56.     {
    57.         DirectoryInfoHolderDictionary.Clear();
    58.  
    59.         foreach (var path in AllPaths)
    60.         {
    61.             if (path.Length == 0)
    62.             {
    63.                 continue;
    64.             }
    65.  
    66.             if (IsDirectoryPath(path))
    67.             {
    68.                 AddDirectoryPathToDictionary(path);
    69.             }
    70.             else if(IsFilePath(path))
    71.             {
    72.                 FilePaths.Add(path);
    73.  
    74.                 var ParentDirectoryPath = Path.GetDirectoryName(path);
    75.  
    76.                 //ignore empty path; happens when the path is just the file name
    77.                 if(ParentDirectoryPath.Length > 0)
    78.                 {
    79.                     AddDirectoryPathToDictionary(ParentDirectoryPath);
    80.                 }
    81.  
    82.                 DirectoryInfoHolderDictionary[ParentDirectoryPath].AddToChildrenFiles(path);
    83.             }
    84.         }
    85.     }
    86.  
    87.     private void AddDirectoryPathToDictionary(string directoryPath)
    88.     {
    89.         if (DirectoryInfoHolderDictionary.ContainsKey(directoryPath) == false)
    90.         {
    91.             DirectoryInfoHolderDictionary.Add(directoryPath, new DirectoryInfoHolder());
    92.  
    93.             var ParentPath = Path.GetDirectoryName(directoryPath);
    94.  
    95.             if(ParentPath.Length > 0)
    96.             {
    97.                 AddDirectoryPathToDictionary(ParentPath);
    98.                 DirectoryInfoHolderDictionary[ParentPath].AddToChildrenDirectoies(directoryPath);
    99.             }
    100.         }
    101.     }
    102.  
    103.     private bool IsDirectoryPath(string path)
    104.     {
    105.         return Path.GetExtension(path).Length == 0;
    106.     }
    107.  
    108.     private bool IsFilePath(string path)
    109.     {
    110.         return Path.GetExtension(path).Length > 0;
    111.     }
    112.  
    113.     public bool DirectoryPathExists(string directoryPath)
    114.     {
    115.         return DirectoryInfoHolderDictionary.ContainsKey(directoryPath);
    116.     }
    117.  
    118.     public bool FilePathExists(string path)
    119.     {
    120.         return FilePaths.Contains(path);
    121.     }
    122.  
    123.     public bool PathExists(string path)
    124.     {
    125.         if (DirectoryPathExists(path))
    126.         {
    127.             return true;
    128.         }
    129.         else
    130.         {
    131.             // AllPaths contain file paths, which is the only known source of file paths in the library
    132.             return AllPaths.Contains(path);
    133.         }
    134.     }
    135.  
    136.     /// <summary>
    137.     /// return all the sub-directories paths that belong to this directoryPath
    138.     /// </summary>
    139.     /// <param name="directoryPath"></param>
    140.     /// <param name="includeSubDirectories">if set to true, do a recursive search return the all the sub-directories paths</param>
    141.     /// <returns></returns>
    142.     public HashSet<string> GetChildrenDirectoriesPaths(string directoryPath, bool includeSubDirectories = false)
    143.     {
    144.         if(DirectoryInfoHolderDictionary.ContainsKey(directoryPath) == false)
    145.         {
    146.             return new HashSet<string>();
    147.         }
    148.  
    149.         HashSet<string> Result = DirectoryInfoHolderDictionary[directoryPath].GetChildrenDirectories();
    150.  
    151.         if (includeSubDirectories)
    152.         {
    153.             foreach(var childrenDirectoryPath in DirectoryInfoHolderDictionary[directoryPath].GetChildrenDirectories())
    154.             {
    155.                 if (DirectoryInfoHolderDictionary.ContainsKey(childrenDirectoryPath))
    156.                 {
    157.                     Result.UnionWith(this.GetChildrenDirectoriesPaths(childrenDirectoryPath, true));
    158.                 }
    159.             }
    160.         }
    161.  
    162.         return Result;
    163.     }
    164.  
    165.     /// <summary>
    166.     /// return all the sub-file paths that belong to this directoryPath
    167.     /// </summary>
    168.     /// <param name="directoryPath"></param>
    169.     /// <param name="includeSubDirectories">if set to true, do a recursive search return the all the sub-file paths</param>
    170.     /// <returns></returns>
    171.     public HashSet<string> GetChildrenFilePaths(string directoryPath, bool includeSubDirectories = false)
    172.     {
    173.         if (DirectoryInfoHolderDictionary.ContainsKey(directoryPath) == false)
    174.         {
    175.             return new HashSet<string>();
    176.         }
    177.  
    178.         HashSet<string> Result = DirectoryInfoHolderDictionary[directoryPath].GetChildrenFiles();
    179.  
    180.         if (includeSubDirectories)
    181.         {
    182.             HashSet<string> SubDirectories = GetChildrenDirectoriesPaths(directoryPath, true);
    183.  
    184.             foreach(var directroy in SubDirectories)
    185.             {
    186.                 if (DirectoryInfoHolderDictionary.ContainsKey(directroy))
    187.                     Result.UnionWith(DirectoryInfoHolderDictionary[directroy].GetChildrenFiles());
    188.             }
    189.         }
    190.  
    191.         return Result;
    192.     }
    193.  
    194.     /// <summary>
    195.     /// return all the paths (directories and files) in the directoryPath
    196.     /// </summary>
    197.     /// <param name="directoryPath"></param>
    198.     /// <param name="includeSubDirectories">if set to true, do a recursive search return the all the sub-file paths</param>
    199.     /// <returns></returns>
    200.     public HashSet<string> GetChildrenPaths(string directoryPath, bool includeSubDirectories = false)
    201.     {
    202.         if (DirectoryInfoHolderDictionary.ContainsKey(directoryPath) == false)
    203.         {
    204.             return new HashSet<string>();
    205.         }
    206.  
    207.         HashSet<string> Result = DirectoryInfoHolderDictionary[directoryPath].GetChildrenDirectories();
    208.         Result.UnionWith(DirectoryInfoHolderDictionary[directoryPath].GetChildrenFiles());
    209.  
    210.         if (includeSubDirectories)
    211.         {
    212.             Result.UnionWith(GetChildrenFilePaths(directoryPath, true));
    213.             Result.UnionWith(GetChildrenDirectoriesPaths(directoryPath, true));
    214.         }
    215.  
    216.         return Result;
    217.     }
    218. }

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.AddressableAssets;
    5. using UnityEngine.ResourceManagement.AsyncOperations;
    6. using Cysharp.Threading.Tasks;
    7. using System.Linq;
    8.  
    9. public static class AddressablePathLookUp
    10. {
    11.     private static bool HasLoaded = false;
    12.  
    13.     private static GenericDirectoryPathLibrary AddressableAddressLibrary;
    14.  
    15.     /// <summary>
    16.     /// Call this externally to reload the library for whatever reason you want
    17.     /// </summary>
    18.     /// <returns></returns>
    19.     public static async UniTask ForceReloadLibrary()
    20.     {
    21.         HasLoaded = false;
    22.         await LoadAddresses();
    23.     }
    24.  
    25.     private static async UniTask LoadAddresses()
    26.     {
    27.         if(HasLoaded == false)
    28.         {
    29.             var Addresses = await Addressables.LoadResourceLocationsAsync("Everything");
    30.             HashSet<string> Result = new HashSet<string>(Addresses.Select(x => x.PrimaryKey));
    31.  
    32.             AddressableAddressLibrary = new GenericDirectoryPathLibrary(Result);
    33.  
    34.             Addressables.Release(Addresses);
    35.  
    36.             HasLoaded = true;
    37.         }
    38.     }
    39.  
    40.     public async static UniTask<bool> AddressablePathExistsAsync(string address)
    41.     {
    42.         await LoadAddresses();
    43.         return AddressableAddressLibrary.PathExists(address);
    44.     }
    45.  
    46.     public async static UniTask<List<string>> GetAllFilePathsInDirectoryAddress(string directoryAddress)
    47.     {
    48.         await LoadAddresses();
    49.         return AddressableAddressLibrary.GetChildrenFilePaths(directoryAddress, false).ToList();
    50.     }
    51. }
    52.  
    Label everything in your Addressables with the "Everything" label, and you can call AddressablePathExistsAsync(string address) and GetFilePathsInDirectoryAddress(string directoryAddress) where ever you want.