WuhuIslandTesting/Library/PackageCache/com.unity.addressables@1.21.12/Editor/Build/AddressableAnalytics.cs
2025-01-07 02:06:59 +01:00

494 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor.AddressableAssets.Build;
using UnityEditor.AddressableAssets.Build.DataBuilders;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
using UnityEngine.Analytics;
using UnityEngine.ResourceManagement.Util;
namespace UnityEditor.AddressableAssets
{
internal static class AddressableAnalytics
{
private const string VendorKey = "unity.addressables";
private static HashSet<string> _registeredEvents = new HashSet<string>();
private const string UsageEvent = "addressablesUsageEvent";
private const string BuildEvent = "addressablesBuildEvent";
[Serializable]
internal struct BuildData
{
public bool IsUsingCCD;
public bool IsContentUpdateBuild;
public bool DebugBuildLayoutEnabled;
public bool AutoOpenBuildReportEnabled;
public bool BuildAndRelease;
public bool IsPlayModeBuild;
public int BuildScript;
public int NumberOfAddressableAssets;
public int NumberOfLabels;
public int NumberOfAssetBundles;
public int NumberOfGroups;
public int NumberOfGroupsUsingLZ4;
public int NumberOfGroupsUsingLZMA;
public int NumberOfGroupsUncompressed;
public int NumberOfGroupsPackedTogether;
public int NumberOfGroupsPackedTogetherByLabel;
public int NumberOfGroupsPackedSeparately;
public int MaxNumberOfAddressableAssetsInAGroup;
public int MinNumberOfAddressableAssetsInAGroup;
public int BuildTarget;
public int NumberOfGroupsUsingEditorHosted;
public int NumberOfGroupsUsingBuiltIn;
public int NumberOfGroupsUsingCCD;
public int NumberOfGroupsUsingRemoteCustomPaths;
public int NumberOfGroupsUsingLocalCustomPaths;
public int NumberOfAssetsInEditorHostedPaths;
public int NumberOfAssetsInBuiltInPaths;
public int NumberOfAssetsInCCDPaths;
public int NumberOfAssetsInRemoteCustomPaths;
public int NumberOfAssetsInLocalCustomPaths;
public int IsIncrementalBuild;
public int ErrorCode;
public double TotalBuildTime;
}
private static string GetSessionStateKeyByUsageEventType(UsageEventType uet)
{
switch (uet)
{
case UsageEventType.OpenAnalyzeWindow:
return "Addressables/Analyze";
case UsageEventType.OpenGroupsWindow:
return "Addressables/Groups";
case UsageEventType.OpenHostingWindow:
return "Addressables/Hosting";
case UsageEventType.OpenProfilesWindow:
return "Addressables/Profiles";
case UsageEventType.OpenEventViewerWindow:
return "Addressables/EventViewer";
default:
return null;
}
}
internal struct UsageData
{
public int UsageEventType;
public bool IsUsingCCD;
public int AutoRunRestrictionsOption;
}
internal enum UsageEventType
{
OpenGroupsWindow = 0,
OpenProfilesWindow = 1,
OpenEventViewerWindow = 2,
OpenAnalyzeWindow = 3,
OpenHostingWindow = 4,
RunBundleLayoutPreviewRule = 5,
RunCheckBundleDupeDependenciesRule = 6,
RunCheckResourcesDupeDependenciesRule = 7,
RunCheckSceneDupeDependenciesRule = 8,
InstallCCDManagementPackage = 9,
ContentUpdateCancelled = 10,
ContentUpdateHasChangesInUpdateRestrictionWindow = 11,
ContentUpdateContinuesWithoutChanges = 12,
RunContentUpdateBuild = 13,
CannotLocateBinFile = 14,
BuildFailedDueToModifiedStaticEntries = 15,
BuildInterruptedDueToStaticModifiedEntriesInUpdate = 16,
OpenBuildReportManually = 17,
BuildReportSelectedExploreTab = 18,
BuildReportSelectedPotentialIssuesTab = 19,
BuildReportSelectedSummaryTab = 20,
BuildReportViewByAssetBundle = 21,
BuildReportViewByAssets = 22,
BuildReportViewByLabels = 23,
BuildReportViewByGroup = 24,
BuildReportViewByDuplicatedAssets = 25,
BuildReportImportedManually = 26
}
internal enum BuildScriptType
{
PackedMode = 0,
PackedPlayMode = 1,
FastMode = 2,
VirtualMode = 3,
CustomBuildScript = 4
}
internal enum PathType
{
BuiltIn = 0,
EditorHosted = 1,
CCD = 2,
Custom = 3,
Automatic = 4
}
internal enum BuildType
{
Inconclusive = -1,
CleanBuild = 0,
IncrementalBuild = 1
}
internal enum ErrorType
{
NoError = 0,
GenericError = 1
}
internal enum AnalyticsContentUpdateRestriction
{
NotApplicable = -1,
ListUpdatedAssetsWithRestrictions = 0,
FailBuild = 1,
Disabled = 2
}
private static bool EventIsRegistered(string eventName)
{
return _registeredEvents.Contains(eventName);
}
//Check if the build cache exists so we know if a build is incremental or clean. May return inconclusive if reflection fails
internal static BuildType DetermineBuildType()
{
try
{
FieldInfo cachePathField = typeof(BuildCache).GetField("k_CachePath", BindingFlags.Static | BindingFlags.NonPublic);
if (cachePathField == null)
return BuildType.Inconclusive;
string cachePath = (string)cachePathField.GetValue(null);
if (cachePath != null && Directory.Exists(cachePath))
return BuildType.IncrementalBuild;
if (cachePath != null)
return BuildType.CleanBuild;
return BuildType.Inconclusive;
}
catch
{
return BuildType.Inconclusive;
}
}
internal static BuildScriptType DetermineBuildScriptType(IDataBuilder buildScript)
{
if (buildScript == null)
return BuildScriptType.CustomBuildScript;
var type = buildScript.GetType();
if (type == typeof(BuildScriptPackedMode))
return BuildScriptType.PackedMode;
if (type == typeof(BuildScriptFastMode))
return BuildScriptType.FastMode;
if (type == typeof(BuildScriptPackedPlayMode))
return BuildScriptType.PackedPlayMode;
if (type == typeof(BuildScriptVirtualMode))
return BuildScriptType.VirtualMode;
return BuildScriptType.CustomBuildScript;
}
internal static ErrorType ParseError(string error)
{
if (String.IsNullOrEmpty(error))
return ErrorType.NoError;
return ErrorType.GenericError;
}
internal static BuildData GenerateBuildData(AddressablesDataBuilderInput builderInput, AddressableAssetBuildResult result, BuildType buildType)
{
AddressableAssetSettings currentSettings = builderInput.AddressableSettings;
bool isContentUpdateBuild = builderInput.IsContentUpdateBuild;
bool isBuildAndRelease = builderInput.IsBuildAndRelease;
bool usingCCD = false;
string error = result.Error;
bool isPlayModeBuild = result is AddressablesPlayModeBuildResult;
double totalBuildDurationSeconds = result.Duration;
int numberOfAssetBundles = -1;
if (result is AddressablesPlayerBuildResult buildRes)
{
numberOfAssetBundles = buildRes.AssetBundleBuildResults.Count;
}
#if ENABLE_CCD
usingCCD = true;
#endif
ErrorType errorCode = ParseError(error);
if (isPlayModeBuild)
{
BuildScriptType playModeBuildScriptType = DetermineBuildScriptType(currentSettings.ActivePlayModeDataBuilder);
BuildData playModeBuildData = new BuildData()
{
IsPlayModeBuild = true,
BuildScript = (int)playModeBuildScriptType
};
return playModeBuildData;
}
BuildScriptType buildScriptType = DetermineBuildScriptType(currentSettings.ActivePlayerDataBuilder);
int numberOfAddressableAssets = 0;
int numberOfGroupsUncompressed = 0;
int numberOfGroupsUsingLZMA = 0;
int numberOfGroupsUsingLZ4 = 0;
int numberOfGroupsPackedSeparately = 0;
int numberOfGroupsPackedTogether = 0;
int numberOfGroupsPackedTogetherByLabel = 0;
int minNumberOfAssetsInAGroup = -1;
int maxNumberOfAssetsInAGroup = -1;
int numberOfGroupsUsingEditorHosted = 0;
int numberOfGroupsUsingBuiltIn = 0;
int numberOfGroupsUsingCCD = 0;
int numberOfGroupsUsingRemoteCustomPaths = 0;
int numberOfGroupsUsingLocalCustomPaths = 0;
int numberOfAssetsInEditorHostedPaths = 0;
int numberOfAssetsInBuiltInPaths = 0;
int numberOfAssetsInCCDPaths = 0;
int numberOfAssetsInRemoteCustomPaths = 0;
int numberOfAssetsInLocalCustomPaths = 0;
List<ProfileGroupType> groupTypes = ProfileGroupType.CreateGroupTypes(currentSettings.profileSettings.GetProfile(currentSettings.activeProfileId), currentSettings);
var dataSourceSettings = ProfileDataSourceSettings.GetSettings();
Dictionary<string, PathType> prefixToTypeMap = new Dictionary<string, PathType>();
foreach (var groupType in groupTypes)
{
ProfileGroupType groupTypeArchetype = dataSourceSettings.FindGroupType(groupType);
if (groupTypeArchetype == null)
prefixToTypeMap.Add(groupType.GroupTypePrefix, PathType.Custom);
else if (groupTypeArchetype.GroupTypePrefix == "Built-In")
prefixToTypeMap.Add(groupType.GroupTypePrefix, PathType.BuiltIn);
else if (groupTypeArchetype.GroupTypePrefix == "Editor Hosted")
prefixToTypeMap.Add(groupType.GroupTypePrefix, PathType.EditorHosted);
else if (groupTypeArchetype.GroupTypePrefix.StartsWith("CCD", StringComparison.Ordinal))
prefixToTypeMap.Add(groupType.GroupTypePrefix, PathType.CCD);
else if (groupTypeArchetype.GroupTypePrefix.StartsWith("Automatic", StringComparison.Ordinal))
prefixToTypeMap.Add(groupType.GroupTypePrefix, PathType.CCD);
}
HashSet<string> vars = currentSettings.profileSettings.GetAllVariableIds();
foreach (var group in currentSettings.groups)
{
if (group == null)
continue;
numberOfAddressableAssets += group.entries.Count;
var schema = group.GetSchema<BundledAssetGroupSchema>();
if (schema == null)
continue;
int selected = schema.DetermineSelectedIndex(groupTypes, -1, currentSettings, vars);
PathType pathType;
if (selected == -1)
pathType = PathType.Custom;
else
pathType = prefixToTypeMap[groupTypes[selected].GroupTypePrefix];
if (pathType == PathType.Custom)
{
if (ResourceManagerConfig.IsPathRemote(schema.LoadPath.GetValue(currentSettings)))
{
numberOfGroupsUsingRemoteCustomPaths += 1;
numberOfAssetsInRemoteCustomPaths += group.entries.Count;
}
else
{
numberOfGroupsUsingLocalCustomPaths += 1;
numberOfAssetsInLocalCustomPaths += group.entries.Count;
}
}
if (pathType == PathType.BuiltIn)
{
numberOfGroupsUsingBuiltIn += 1;
numberOfAssetsInBuiltInPaths += group.entries.Count;
}
if (pathType == PathType.EditorHosted)
{
numberOfGroupsUsingEditorHosted += 1;
numberOfAssetsInEditorHostedPaths += group.entries.Count;
}
if (pathType == PathType.CCD)
{
numberOfGroupsUsingCCD += 1;
numberOfAssetsInCCDPaths += group.entries.Count;
}
if (pathType == PathType.Automatic)
{
numberOfGroupsUsingCCD += 1;
numberOfAssetsInCCDPaths += group.entries.Count;
}
var bundleMode = schema.BundleMode;
var compressionType = schema.Compression;
switch (compressionType)
{
case BundledAssetGroupSchema.BundleCompressionMode.Uncompressed:
numberOfGroupsUncompressed += 1;
break;
case BundledAssetGroupSchema.BundleCompressionMode.LZ4:
numberOfGroupsUsingLZ4 += 1;
break;
case BundledAssetGroupSchema.BundleCompressionMode.LZMA:
numberOfGroupsUsingLZMA += 1;
break;
}
switch (bundleMode)
{
case BundledAssetGroupSchema.BundlePackingMode.PackSeparately:
numberOfGroupsPackedSeparately += 1;
break;
case BundledAssetGroupSchema.BundlePackingMode.PackTogether:
numberOfGroupsPackedTogether += 1;
break;
case BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel:
numberOfGroupsPackedTogetherByLabel += 1;
break;
}
if (group.entries.Count > maxNumberOfAssetsInAGroup)
maxNumberOfAssetsInAGroup = group.entries.Count;
if (minNumberOfAssetsInAGroup == -1 || group.entries.Count < minNumberOfAssetsInAGroup)
minNumberOfAssetsInAGroup = group.entries.Count;
}
BuildData data = new BuildData()
{
IsUsingCCD = usingCCD,
IsContentUpdateBuild = isContentUpdateBuild,
IsPlayModeBuild = false,
BuildScript = (int)buildScriptType,
BuildAndRelease = isBuildAndRelease,
#if UNITY_2022_2_OR_NEWER
DebugBuildLayoutEnabled = ProjectConfigData.GenerateBuildLayout,
AutoOpenBuildReportEnabled = ProjectConfigData.AutoOpenAddressablesReport && ProjectConfigData.GenerateBuildLayout,
#endif
NumberOfLabels = currentSettings.labelTable.labelNames.Count,
IsIncrementalBuild = (int)buildType,
NumberOfAssetBundles = numberOfAssetBundles,
NumberOfAddressableAssets = numberOfAddressableAssets,
MinNumberOfAddressableAssetsInAGroup = minNumberOfAssetsInAGroup,
MaxNumberOfAddressableAssetsInAGroup = maxNumberOfAssetsInAGroup,
NumberOfGroups = currentSettings.groups.Count,
TotalBuildTime = totalBuildDurationSeconds,
NumberOfGroupsUsingLZ4 = numberOfGroupsUsingLZ4,
NumberOfGroupsUsingLZMA = numberOfGroupsUsingLZMA,
NumberOfGroupsUncompressed = numberOfGroupsUncompressed,
NumberOfGroupsPackedTogether = numberOfGroupsPackedTogether,
NumberOfGroupsPackedTogetherByLabel = numberOfGroupsPackedTogetherByLabel,
NumberOfGroupsPackedSeparately = numberOfGroupsPackedSeparately,
NumberOfGroupsUsingBuiltIn = numberOfGroupsUsingBuiltIn,
NumberOfGroupsUsingEditorHosted = numberOfGroupsUsingEditorHosted,
NumberOfGroupsUsingRemoteCustomPaths = numberOfGroupsUsingRemoteCustomPaths,
NumberOfGroupsUsingLocalCustomPaths = numberOfGroupsUsingLocalCustomPaths,
NumberOfGroupsUsingCCD = numberOfGroupsUsingCCD,
NumberOfAssetsInRemoteCustomPaths = numberOfAssetsInRemoteCustomPaths,
NumberOfAssetsInLocalCustomPaths = numberOfAssetsInLocalCustomPaths,
NumberOfAssetsInBuiltInPaths = numberOfAssetsInBuiltInPaths,
NumberOfAssetsInEditorHostedPaths = numberOfAssetsInEditorHostedPaths,
NumberOfAssetsInCCDPaths = numberOfAssetsInCCDPaths,
BuildTarget = (int)EditorUserBuildSettings.activeBuildTarget,
ErrorCode = (int)errorCode
};
return data;
}
internal static void ReportBuildEvent(AddressablesDataBuilderInput builderInput, AddressableAssetBuildResult result, BuildType buildType)
{
if (!EditorAnalytics.enabled)
return;
if (!EventIsRegistered(BuildEvent))
if (!RegisterEvent(BuildEvent))
return;
BuildData data = GenerateBuildData(builderInput, result, buildType);
EditorAnalytics.SendEventWithLimit(BuildEvent, data);
}
internal static UsageData GenerateUsageData(UsageEventType eventType, AnalyticsContentUpdateRestriction restriction = AnalyticsContentUpdateRestriction.NotApplicable)
{
bool usingCCD = false;
#if ENABLE_CCD
usingCCD = true;
#endif
var data = new UsageData()
{
UsageEventType = (int)eventType,
IsUsingCCD = usingCCD,
AutoRunRestrictionsOption = (int)restriction
};
return data;
}
internal static void ReportUsageEvent(UsageEventType eventType, bool limitEventOncePerSession = false, int contentUpdateRestriction = -1)
{
if (!EditorAnalytics.enabled)
return;
var sessionStateKey = GetSessionStateKeyByUsageEventType(eventType);
if (limitEventOncePerSession && sessionStateKey != null && SessionState.GetBool(sessionStateKey, false))
return;
if (!SessionState.GetBool(sessionStateKey, false))
SessionState.SetBool(sessionStateKey, true);
if (!EventIsRegistered(UsageEvent))
if (!RegisterEvent(UsageEvent))
return;
UsageData data = GenerateUsageData(eventType, (AnalyticsContentUpdateRestriction) contentUpdateRestriction);
EditorAnalytics.SendEventWithLimit(UsageEvent, data);
}
private static bool RegisterEvent(string eventName)
{
bool eventSuccessfullyRegistered = false;
AnalyticsResult registerEvent = EditorAnalytics.RegisterEventWithLimit(eventName, 100, 100, VendorKey);
if (registerEvent == AnalyticsResult.Ok)
{
_registeredEvents.Add(eventName);
eventSuccessfullyRegistered = true;
}
return eventSuccessfullyRegistered;
}
}
}