using System; using System.Reflection; using UnityEditor; using UnityEngine; public static class PackageManagerHelper { const string ServicesNamespace = "UnityEditor.PackageManager.UI.Internal"; /// /// Resolves a PackageManager service by its interface name. /// Locates the service in the internal ServicesContainer based on the given interface name /// and returns the instance of the resolved service. /// /// The name of the interface to resolve (e.g., "IPackageDatabase"). /// The resolved service instance corresponding to the interface name. /// Thrown if any required type, property, or method cannot be found /// or if the service cannot be resolved. static object ResolveService(string interfaceName) { try { // Retrieve the ServicesContainer type var servicesContainerType = Type.GetType($"{ServicesNamespace}.ServicesContainer, UnityEditor") ?? throw new Exception($"Failed to find {ServicesNamespace}.ServicesContainer type."); // Get the instance property from ScriptableSingleton var instanceProperty = typeof(ScriptableSingleton<>).MakeGenericType(servicesContainerType) .GetProperty("instance", BindingFlags.Static | BindingFlags.Public) ?? throw new Exception("Failed to find 'instance' property on ScriptableSingleton."); var servicesContainerInstance = instanceProperty.GetValue(null) ?? throw new Exception("Failed to get ServicesContainer instance."); // Retrieve the Resolve method var resolveMethod = servicesContainerType.GetMethod("Resolve", BindingFlags.Instance | BindingFlags.Public) ?? throw new Exception("Failed to find 'Resolve' method on ServicesContainer."); // Resolve the interface type dynamically var interfaceType = Type.GetType($"{ServicesNamespace}.{interfaceName}, UnityEditor") ?? throw new Exception($"Failed to find interface type: {ServicesNamespace}.{interfaceName}."); // Invoke the generic Resolve method return resolveMethod.MakeGenericMethod(interfaceType).Invoke(servicesContainerInstance, null); } catch (Exception ex) { throw new Exception($"Error resolving service '{interfaceName}': {ex.Message}"); } } /// /// Imports a package from the Package Manager by its name. /// Uses the Unity Package Manager services to locate and import the package by triggering its installation. /// /// The name of the package to import (e.g., "com.unity.textmeshpro"). /// /// `true` if the package is successfully imported; /// `false` if the service cannot be resolved, the package is not found, or an error occurs during installation. /// /// Thrown if any required service, method, or package property cannot be resolved. public static bool ImportPackageByName(string packageName) { try { // Resolve IPackageOperationDispatcher var dispatcher = ResolveService("IPackageOperationDispatcher") ?? throw new Exception("Failed to resolve IPackageOperationDispatcher."); // Find package by name var package = FindPackageByName(packageName) ?? throw new Exception($"Package '{packageName}' not found in the cache."); // Retrieve m_AssetStorePackageInstaller var installerField = dispatcher.GetType() .GetField("m_AssetStorePackageInstaller", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new Exception("Failed to find m_AssetStorePackageInstaller field on IPackageOperationDispatcher."); var assetStorePackageInstaller = installerField.GetValue(dispatcher) ?? throw new Exception("Failed to retrieve m_AssetStorePackageInstaller instance."); // Retrieve Install method var installMethod = assetStorePackageInstaller.GetType() .GetMethod("Install", BindingFlags.Instance | BindingFlags.Public) ?? throw new Exception("Failed to find Install method on AssetStorePackageInstaller."); // Resolve product and productId var product = package.GetType().GetProperty("product", BindingFlags.Instance | BindingFlags.Public) ?.GetValue(package) ?? throw new Exception("Product information is null."); var productId = Convert.ToInt64( product.GetType().GetProperty("id", BindingFlags.Instance | BindingFlags.Public) ?.GetValue(product) ?? throw new Exception("Failed to retrieve 'id' property on package product.") ); // Invoke the install method installMethod.Invoke(assetStorePackageInstaller, new object[] { productId, false }); Debug.Log($"Package '{packageName}' imported successfully."); return true; } catch (Exception ex) { Debug.LogError($"Error importing package '{packageName}': {ex.Message}"); return false; } } /// /// Finds a package in the Unity Package Manager by its name. /// Resolves the internal PackageCache and retrieves the package along with its version details. /// /// The name of the package to search for (e.g., "com.unity.textmeshpro"). /// /// The resolved package object if found; /// `null` if the service, method, or package cannot be resolved. /// /// Thrown if any required service, method, or package property cannot be resolved. static object FindPackageByName(string packageName) { try { // Resolve the internal PackageDatabase var packageDatabase = ResolveService("IPackageDatabase") ?? throw new Exception("Failed to resolve IPackageDatabase."); // Retrieve the GetPackageAndVersionByIdOrName method var getPackageMethod = packageDatabase.GetType().GetMethod( "GetPackageAndVersionByIdOrName", BindingFlags.Instance | BindingFlags.Public ) ?? throw new Exception("Failed to find GetPackageAndVersionByIdOrName method on IPackageDatabase."); // Prepare parameters and invoke the method object[] parameters = { packageName, null, null, true }; getPackageMethod.Invoke(packageDatabase, parameters); return parameters[1]; // Return the resolved package } catch (Exception ex) { Debug.LogError($"Error while resolving package '{packageName}': {ex.Message}"); return null; } } /// /// Searches the user's Asset Store purchases based on a provided search text /// and ensures the results are loaded into the PackageManager cache. /// Utilizes Unity's Asset Store-related services to query purchased assets /// and populate the cache for further use. /// /// The text to search for in the Asset Store purchases (e.g., "AutoRef - Constants in Code"). /// Thrown if any required service or method cannot be resolved while executing the search. public static void SearchAssetStorePurchases(string searchText) { try { // Resolve AssetStoreClientV2 var assetStoreClient = ResolveService("AssetStoreClientV2") ?? throw new Exception("Failed to resolve AssetStoreClientV2."); // Create PurchasesQueryArgs with the search text var purchasesQueryArgsType = typeof(ScriptableSingleton<>).Assembly.GetType("UnityEditor.PackageManager.UI.Internal.PurchasesQueryArgs") ?? throw new Exception("Failed to find PurchasesQueryArgs type."); var queryArgsConstructor = purchasesQueryArgsType.GetConstructor( new[] { typeof(int), typeof(int), typeof(string), Type.GetType("UnityEditor.PackageManager.UI.Internal.PageFilters, UnityEditor") } ) ?? throw new Exception("Failed to find PurchasesQueryArgs constructor."); var queryArgs = queryArgsConstructor.Invoke(new object[] { 0, 0, searchText, null }); // Call ListPurchases var listPurchasesMethod = assetStoreClient.GetType().GetMethod("ListPurchases", BindingFlags.Instance | BindingFlags.Public) ?? throw new Exception("Failed to find ListPurchases method on AssetStoreClientV2."); listPurchasesMethod.Invoke(assetStoreClient, new object[] { queryArgs }); Debug.Log($"Successfully triggered a search for '{searchText}' in the Asset Store."); } catch (Exception ex) { Debug.LogError($"Error searching Asset Store purchases: {ex.Message}"); } } // Testing only public static void ClearAssetStoreOnlineCache() { try { // Resolve AssetStoreClientV2 and retrieve m_AssetStoreCache var assetStoreClient = ResolveService("AssetStoreClientV2") ?? throw new Exception("Failed to resolve AssetStoreClientV2."); var cacheField = assetStoreClient.GetType().GetField("m_AssetStoreCache", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new Exception("m_AssetStoreCache field not found on AssetStoreClientV2."); var assetStoreCache = cacheField.GetValue(assetStoreClient) ?? throw new Exception("m_AssetStoreCache instance is null."); // Find and invoke ClearOnlineCache from m_AssetStoreCache var clearCacheMethod = assetStoreCache.GetType().GetMethod("ClearOnlineCache", BindingFlags.Instance | BindingFlags.Public) ?? throw new Exception("ClearOnlineCache method not found on IAssetStoreCache."); clearCacheMethod.Invoke(assetStoreCache, null); Debug.Log("Successfully cleared the Asset Store online cache."); } catch (Exception ex) { Debug.LogError($"Error clearing Asset Store cache: {ex.Message}"); } } }