A game about forced loneliness, made by TACStudios
1//#define ANALYTICS_DEBUG
2using System;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Linq;
6using System.Text;
7using UnityEngine.Analytics;
8using UnityEngine.Timeline;
9
10namespace UnityEditor.Timeline
11{
12 class TimelineWindowAnalytics
13 {
14 const string vendorKey = "unity.timeline";
15 const string eventName = "timeline_editor_info";
16 const int version = 2;
17 const int maxEventsPerHour = 1000;
18 const int maxNumberOfElements = 1000;
19
20 [Serializable]
21 internal struct timeline_asset_stats
22#if UNITY_2023_2_OR_NEWER
23 : IAnalytic.IData
24#endif
25 {
26 public string asset_guid;
27 public double duration;
28 public double frame_rate;
29 public List<track_asset_stats> track_stats;
30 public double mix_samples_count, ripple_samples_count, replace_samples_count;
31 public string display_format;
32 }
33
34 [Serializable]
35 internal struct track_asset_stats
36 {
37 public string track_type;
38 public int clip_count;
39 public int marker_count;
40 }
41
42 class WindowAnalyticsStats
43 {
44 internal int[] editModeSamples = new int[3]; // EditModes
45 }
46
47#if UNITY_2023_2_OR_NEWER
48 [AnalyticInfo(
49 eventName: eventName,
50 vendorKey: vendorKey,
51 version: version,
52 maxEventsPerHour: maxEventsPerHour,
53 maxNumberOfElements: maxNumberOfElements)]
54 class TimelineWindowAnalyticsEvent : IAnalytic
55 {
56 public timeline_asset_stats timelineStats { get; }
57 bool m_CanSendData;
58
59 public TimelineWindowAnalyticsEvent()
60 {
61 m_CanSendData = GenerateTimelineAssetStats(out timeline_asset_stats data);
62 timelineStats = data;
63 }
64
65 public void Send()
66 {
67 if (m_CanSendData)
68 EditorAnalytics.SendAnalytic(this);
69 }
70
71 bool IAnalytic.TryGatherData(out IAnalytic.IData data, out Exception error)
72 {
73 error = null;
74 data = timelineStats;
75 return true;
76 }
77 }
78#endif
79
80 static WindowAnalyticsStats analyticsStats = new WindowAnalyticsStats();
81
82 public void SendPlayEvent(bool start)
83 {
84 if (!start || !EditorAnalytics.enabled)
85 return;
86
87#if UNITY_2023_2_OR_NEWER
88 var analyticsEvent = new TimelineWindowAnalyticsEvent();
89 LogAnalyticsData(analyticsEvent.timelineStats);
90 analyticsEvent.Send();
91#else
92 EditorAnalytics.RegisterEventWithLimit(eventName, maxEventsPerHour, maxNumberOfElements, vendorKey, version);
93 var ret = GenerateTimelineAssetStats(out var data);
94 if (!ret)
95 return;
96 LogAnalyticsData(data);
97 EditorAnalytics.SendEventWithLimit(eventName, data, version);
98#endif
99 SendAfterSequenceChangeEvent();
100 }
101
102 public void SendAfterSequenceChangeEvent()
103 {
104 analyticsStats = new WindowAnalyticsStats(); // Wipe Window Stats
105 }
106
107 public void SendManipulationEndedEvent()
108 {
109 analyticsStats.editModeSamples[(int)EditMode.editType]++;
110 }
111
112 internal static bool GenerateTimelineAssetStats(out timeline_asset_stats data)
113 {
114 var timeline = TimelineEditor.inspectedAsset;
115 if (timeline == null ||
116 !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(timeline, out var guid, out long _))
117 {
118 data = new timeline_asset_stats();
119 return false;
120 }
121
122 data = new timeline_asset_stats
123 {
124 asset_guid = guid,
125 duration = timeline.duration,
126 frame_rate = timeline.editorSettings.frameRate,
127 track_stats = GetTrackAssetStats(timeline),
128 display_format = TimelinePreferences.instance.timeFormat.ConvertToString(),
129 mix_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Mix],
130 ripple_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Ripple],
131 replace_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Replace],
132 };
133
134 return true;
135 }
136
137 static List<track_asset_stats> GetTrackAssetStats(TimelineAsset timeline)
138 {
139 var ret = new List<track_asset_stats>();
140 foreach (var track in timeline.flattenedTracks)
141 {
142 ret.Add(new track_asset_stats
143 {
144 track_type = track.GetType().FullName,
145 clip_count = track.GetClips().Count(),
146 marker_count = track.GetMarkers().Count()
147 }
148 );
149 }
150 return ret;
151 }
152
153 [Conditional("ANALYTICS_DEBUG")]
154 static void LogAnalyticsData(timeline_asset_stats data)
155 {
156 UnityEngine.Debug.Log(UnityEngine.JsonUtility.ToJson(data, true));
157 }
158 }
159
160 static class ConversionUtilities
161 {
162 internal static string ConvertToString<T>(this T e) where T : Enum
163 {
164 return Enum.GetName(typeof(T), e).ToSnakeCase();
165 }
166
167 static string ToSnakeCase(this string str)
168 {
169 var sb = new StringBuilder();
170 for (var i = 0; i < str.Length - 1; ++i)
171 {
172 var ch = str[i];
173 var nCh = str[i + 1];
174 if (char.IsUpper(ch) && char.IsLower(nCh))
175 {
176 sb.Append("_");
177 }
178
179 sb.Append(ch.ToString().ToLower());
180 }
181
182 sb.Append(str[str.Length - 1].ToString().ToLower());
183
184 return sb.ToString().TrimStart('_');
185 }
186 }
187}