WuhuIslandTesting/Library/PackageCache/com.unity.addressables@1.21.12/Editor/Settings/ProfileDataSourceSettings.cs

517 lines
22 KiB
C#
Raw Normal View History

2025-01-07 02:06:59 +01:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using System.Text;
using System.Net.Http;
#if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
using Unity.Services.Core;
using Unity.Services.Ccd.Management;
using Unity.Services.Ccd.Management.Http;
using Unity.Services.Ccd.Management.Models;
#endif
namespace UnityEditor.AddressableAssets.Settings
{
/// <summary>
/// Scriptable Object that holds data source setting information for the profile data source dropdown window
/// </summary>
public class ProfileDataSourceSettings : ScriptableObject, ISerializationCallbackReceiver
{
const string DEFAULT_PATH = "Assets/AddressableAssetsData";
const string DEFAULT_NAME = "ProfileDataSourceSettings";
const string CONTENT_RANGE_HEADER = "Content-Range";
static string DEFAULT_SETTING_PATH = $"{DEFAULT_PATH}/{DEFAULT_NAME}.asset";
#if ENABLE_CCD
internal const string MANAGED_ENVIRONMENT = "ManagedEnvironment";
internal const string MANAGED_BUCKET = "ManagedBucket";
internal const string MANAGED_BADGE = "ManagedBadge";
#endif
// paths
internal static string m_CloudEnvironment = "production";
internal static string m_GenesisBasePath = "https://api-staging.unity.com";
internal static string m_CcdClientBasePath = ".client-api-stg.unity3dusercontent.com";
internal static string m_DashboardBasePath = "https://staging.dashboard.unity3d.com";
internal static string m_ServicesBasePath = "https://staging.services.unity.com";
[InitializeOnLoadMethod]
internal static void InitializeCloudEnvironment()
{
var cloudEnvironment = GetCloudEnvironment();
switch (cloudEnvironment)
{
case "staging":
#if ENABLE_CCD
CcdManagement.SetBasePath("https://staging.services.unity.com");
#endif
m_GenesisBasePath = "https://api-staging.unity.com";
m_CcdClientBasePath = ".client-api-stg.unity3dusercontent.com";
m_DashboardBasePath = "https://staging.dashboard.unity3d.com";
m_ServicesBasePath = "https://staging.services.unity.com";
break;
default:
#if ENABLE_CCD
CcdManagement.SetBasePath("https://services.unity.com");
#endif
m_GenesisBasePath = "https://api.unity.com";
m_CcdClientBasePath = ".client-api.unity3dusercontent.com";
m_DashboardBasePath = "https://dashboard.unity3d.com";
m_ServicesBasePath = "https://services.unity.com";
break;
}
}
const string EnvironmentArg = "-cloudEnvironment";
internal static string GetCloudEnvironment()
{
try
{
var commandLineArgs = System.Environment.GetCommandLineArgs();
var cloudEnvironmentIndex = Array.IndexOf(commandLineArgs, EnvironmentArg);
if (cloudEnvironmentIndex >= 0 && cloudEnvironmentIndex <= commandLineArgs.Length - 2)
{
return commandLineArgs[cloudEnvironmentIndex + 1];
}
}
catch (Exception e)
{
Debug.LogError(e);
}
return null;
}
/// <summary>
/// Group types that exist within the settings object
/// </summary>
[SerializeField]
public List<ProfileGroupType> profileGroupTypes = new List<ProfileGroupType>();
[SerializeField]
internal List<Environment> environments = new List<Environment>();
[SerializeField]
internal Environment currentEnvironment;
/// <summary>
/// Creates, if needed, and returns the profile data source settings for the project
/// </summary>
/// <param name="path">Desired path to put settings</param>
/// <param name="settingName">Desired name for settings</param>
/// <returns></returns>
public static ProfileDataSourceSettings Create(string path = null, string settingName = null)
{
ProfileDataSourceSettings aa;
var assetPath = DEFAULT_SETTING_PATH;
if (path != null && settingName != null)
{
assetPath = $"{path}/{settingName}.asset";
}
aa = AssetDatabase.LoadAssetAtPath<ProfileDataSourceSettings>(assetPath);
if (aa == null)
{
Directory.CreateDirectory(path != null ? path : DEFAULT_PATH);
aa = CreateInstance<ProfileDataSourceSettings>();
AssetDatabase.CreateAsset(aa, assetPath);
aa = AssetDatabase.LoadAssetAtPath<ProfileDataSourceSettings>(assetPath);
aa.profileGroupTypes = CreateDefaultGroupTypes();
EditorUtility.SetDirty(aa);
}
return aa;
}
/// <summary>
/// Gets the profile data source settings for the project
/// </summary>
/// <param name="path"></param>
/// <param name="settingName"></param>
/// <returns></returns>
public static ProfileDataSourceSettings GetSettings(string path = null, string settingName = null)
{
ProfileDataSourceSettings aa;
var assetPath = DEFAULT_SETTING_PATH;
if (path != null && settingName != null)
{
assetPath = $"{path}/{settingName}.asset";
}
aa = AssetDatabase.LoadAssetAtPath<ProfileDataSourceSettings>(assetPath);
if (aa == null)
return Create();
return aa;
}
/// <summary>
/// Creates a list of default group types that are automatically added on ProfileDataSourceSettings object creation
/// </summary>
/// <returns>List of ProfileGroupTypes: Built-In and Editor Hosted</returns>
public static List<ProfileGroupType> CreateDefaultGroupTypes() => new List<ProfileGroupType>
{
CreateBuiltInGroupType(),
CreateEditorHostedGroupType(),
#if ENABLE_CCD
CreateCcdManagerGroupType()
#endif
};
static ProfileGroupType CreateBuiltInGroupType()
{
ProfileGroupType defaultBuiltIn = new ProfileGroupType(AddressableAssetSettings.LocalGroupTypePrefix);
defaultBuiltIn.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, AddressableAssetSettings.kLocalBuildPathValue));
defaultBuiltIn.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, AddressableAssetSettings.kLocalLoadPathValue));
return defaultBuiltIn;
}
static ProfileGroupType CreateEditorHostedGroupType()
{
ProfileGroupType defaultRemote = new ProfileGroupType(AddressableAssetSettings.EditorHostedGroupTypePrefix);
defaultRemote.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, AddressableAssetSettings.kRemoteBuildPathValue));
defaultRemote.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, AddressableAssetSettings.RemoteLoadPathValue));
return defaultRemote;
}
#if ENABLE_CCD
static ProfileGroupType CreateCcdManagerGroupType()
{
string buildPath = $"{AddressableAssetSettings.kCCDBuildDataPath}/{MANAGED_ENVIRONMENT}/{MANAGED_BUCKET}/{MANAGED_BADGE}";
string loadPath =
$"https://{CloudProjectSettings.projectId}{m_CcdClientBasePath}/client_api/v1/environments/{{CcdManager.EnvironmentName}}/buckets/{{CcdManager.BucketId}}/release_by_badge/{{CcdManager.Badge}}/entry_by_path/content/?path=";
ProfileGroupType defaultCcdManager = new ProfileGroupType(AddressableAssetSettings.CcdManagerGroupTypePrefix);
defaultCcdManager.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, buildPath));
defaultCcdManager.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, loadPath));
return defaultCcdManager;
}
#endif
/// <summary>
/// Given a valid profileGroupType, searches the settings and returns, if exists, the profile group type
/// </summary>
/// <param name="groupType"></param>
/// <returns>ProfileGroupType if found, null otherwise</returns>
public ProfileGroupType FindGroupType(ProfileGroupType groupType)
{
ProfileGroupType result = null;
if (!groupType.IsValidGroupType())
{
throw new ArgumentException("Group Type is not valid. Group Type must include a build path and load path variables");
}
var buildPath = groupType.GetVariableBySuffix(AddressableAssetSettings.kBuildPath);
var loadPath = groupType.GetVariableBySuffix(AddressableAssetSettings.kLoadPath);
foreach (ProfileGroupType settingsGroupType in profileGroupTypes)
{
var foundBuildPath = settingsGroupType.ContainsVariable(buildPath);
var foundLoadPath = settingsGroupType.ContainsVariable(loadPath);
if (foundBuildPath && foundLoadPath)
{
result = settingsGroupType;
break;
}
}
return result;
}
/// <summary>
/// Retrieves a list of ProfileGroupType that matches the given prefix
/// </summary>
/// <param name="prefix">prefix to search by</param>
/// <returns>List of ProfileGroupType</returns>
public List<ProfileGroupType> GetGroupTypesByPrefix(string prefix)
{
return profileGroupTypes.Where((groupType) => groupType.GroupTypePrefix.StartsWith(prefix, StringComparison.Ordinal)).ToList();
}
#if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
/// <summary>
/// Updates the CCD buckets and badges with the data source settings
/// </summary>
/// <param name="projectId">Project Id connected to Unity Services</param>
/// <param name="showInfoLog">Whether or not to show debug logs or not</param>
/// <returns>List of ProfileGroupType</returns>
public static async Task<List<ProfileGroupType>> UpdateCCDDataSourcesAsync(string projectId, bool showInfoLog)
{
var settings = GetSettings();
if (showInfoLog)
{
Addressables.Log("Syncing CCD Environments, Buckets, and Badges.");
}
var profileGroupTypes = new List<ProfileGroupType>();
var environments = await GetEnvironments();
if (showInfoLog)
{
EditorUtility.DisplayProgressBar("Syncing Profile Data Sources", "Fetching Environments", 0);
Addressables.Log($"Successfully fetched {environments.Count} environments.");
}
profileGroupTypes.AddRange(CreateDefaultGroupTypes());
try
{
var envProgress = 1;
foreach (var environment in environments)
{
var bucketDictionary = await GetAllBucketsAsync(environment.id);
var bucketProgress = 1;
foreach (var kvp in bucketDictionary)
{
var bucket = kvp.Value;
if (showInfoLog)
{
EditorUtility.DisplayProgressBar($"Syncing Environment: {environment.name} ({envProgress} of {environments.Count})", $"Loading {bucket.Name}", (bucketProgress / (float)bucketDictionary.Count));
}
var badges = await GetAllBadgesAsync(bucket.Id.ToString());
if (badges.Count == 0) badges.Add(new CcdBadge(name: "latest"));
foreach (var badge in badges)
{
var groupType =
new ProfileGroupType($"CCD{ProfileGroupType.k_PrefixSeparator}{projectId}{ProfileGroupType.k_PrefixSeparator}{environment.id}{ProfileGroupType.k_PrefixSeparator}{bucket.Id}{ProfileGroupType.k_PrefixSeparator}{badge.Name}");
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}", bucket.Name));
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Id)}", bucket.Id.ToString()));
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBadge)}{nameof(CcdBadge.Name)}", badge.Name));
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(nameof(CcdBucket.Attributes.PromoteOnly), bucket.Attributes.PromoteOnly.ToString()));
//Adding environment stub here
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.name)}", environment.name));
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.id)}", environment.id));
string buildPath = $"{AddressableAssetSettings.kCCDBuildDataPath}/{environment.id}/{bucket.Id}/{badge.Name}";
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, buildPath));
string loadPath =
$"https://{projectId}{m_CcdClientBasePath}/client_api/v1/environments/{environment.name}/buckets/{bucket.Id}/release_by_badge/{badge.Name}/entry_by_path/content/?path=";
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, loadPath));
profileGroupTypes.Add(groupType);
}
bucketProgress++;
}
envProgress++;
}
settings.profileGroupTypes = profileGroupTypes;
settings.environments = environments.ToList();
if (showInfoLog) Addressables.Log("Successfully synced CCD Buckets and Badges.");
EditorUtility.SetDirty(settings);
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
}
catch (CcdManagementException e)
{
throw e;
}
finally
{
EditorUtility.ClearProgressBar();
}
return settings.profileGroupTypes;
}
internal static async Task<Dictionary<Guid, CcdBucket>> GetAllBucketsAsync(string environment)
{
CcdManagement.SetEnvironmentId(environment);
int page = 1;
bool loop = true;
List<CcdBucket> buckets = new List<CcdBucket>();
do
{
try
{
var listBuckets = await CcdManagement.Instance.ListBucketsAsync(new PageOptions()
{
Page = page
});
buckets.AddRange(listBuckets);
page++;
}
catch (CcdManagementException e)
{
if (e.ErrorCode == CcdManagementErrorCodes.OutOfRange)
{
loop = false;
}
else if (e.ErrorCode == CommonErrorCodes.Forbidden)
{
throw new CcdManagementException(e.ErrorCode, "Unactivated Org. Please activate your organization via the Unity Dashboard");
}
else
{
throw e;
}
}
} while (loop);
return buckets.ToDictionary(kvp => kvp.Id, kvp => kvp);
}
internal static async Task<List<CcdBadge>> GetAllBadgesAsync(string bucketId)
{
int page = 1;
bool loop = true;
List<CcdBadge> badges = new List<CcdBadge>();
do
{
try
{
var listBadges = await CcdManagement.Instance.ListBadgesAsync(Guid.Parse(bucketId), new PageOptions()
{
Page = page
});
badges.AddRange(listBadges);
page++;
}
catch (CcdManagementException e)
{
if (e.ErrorCode == CcdManagementErrorCodes.OutOfRange)
{
loop = false;
}
else
{
throw e;
}
}
} while (loop);
return badges;
}
internal static async Task<List<Environment>> GetEnvironments()
{
var projectId = CloudProjectSettings.projectId;
var authToken = await GetAuthToken();
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", authToken);
var response = await client.GetAsync(String.Format("{0}/api/unity/legacy/v1/projects/{1}/environments", m_ServicesBasePath, projectId));
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.ReasonPhrase);
}
var data = await response.Content.ReadAsStringAsync();
var envs = JsonUtility.FromJson<Environments>(data);
var envList = envs.results.ToList();
return envList;
}
}
private static async Task<string> GetAuthToken()
{
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
{
var jsonString = JsonUtility.ToJson(new Token() { token = CloudProjectSettings.accessToken });
var url = $"{m_ServicesBasePath}/api/auth/v1/genesis-token-exchange/unity/";
var clientResponse = await client.PostAsync(url, new StringContent(jsonString, Encoding.UTF8, "application/json"));
if (!clientResponse.IsSuccessStatusCode)
{
throw new Exception(clientResponse.ReasonPhrase);
}
var token = JsonUtility.FromJson<Token>(await clientResponse.Content.ReadAsStringAsync()).token;
return $"Bearer {token}";
}
}
internal void SetEnvironmentById(string id)
{
Environment env = environments.Where(x => x.id == id).FirstOrDefault();
if (env != null)
{
currentEnvironment = env;
}
else
{
throw new Exception("Unable to find environment by id");
}
}
#endif
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
// Ensure static Group types have the correct string
// Local
var types = GetGroupTypesByPrefix(AddressableAssetSettings.LocalGroupTypePrefix);
if (types == null || types.Count == 0)
profileGroupTypes.Add(CreateBuiltInGroupType());
else
{
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath,
AddressableAssetSettings.kLocalBuildPathValue));
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath,
AddressableAssetSettings.kLocalLoadPathValue));
}
// Editor Hosted
types = GetGroupTypesByPrefix(AddressableAssetSettings.EditorHostedGroupTypePrefix);
if (types.Count == 0)
profileGroupTypes.Add(CreateEditorHostedGroupType());
else
{
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath,
AddressableAssetSettings.kRemoteBuildPathValue));
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath,
AddressableAssetSettings.RemoteLoadPathValue));
}
}
/// <summary>
/// Access Token
/// </summary>
private class Token
{
[SerializeField]
public string token;
}
/// <summary>
/// Environment Wrapper Object
/// </summary>
internal class Environments
{
[SerializeField]
public List<Environment> results;
}
/// <summary>
/// Identity API Environment Object
/// </summary>
[Serializable]
internal class Environment
{
[SerializeField]
public string id;
[SerializeField]
public string projectId;
[SerializeField]
public string projectGenesisId;
[SerializeField]
public string name;
[SerializeField]
public bool isDefault;
}
}
}