A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR
2
3using System;
4using UnityEditor;
5using UnityEngine.Serialization;
6
7namespace UnityEngine.InputSystem.Editor
8{
9#if UNITY_2023_2_OR_NEWER
10 [UnityEngine.Analytics.AnalyticInfo(eventName: kEventName, maxEventsPerHour: kMaxEventsPerHour,
11 maxNumberOfElements: kMaxNumberOfElements, vendorKey: UnityEngine.InputSystem.InputAnalytics.kVendorKey)]
12#endif // UNITY_2023_2_OR_NEWER
13 internal class InputExitPlayModeAnalytic : UnityEngine.InputSystem.InputAnalytics.IInputAnalytic
14 {
15 public const string kEventName = "input_exit_playmode";
16 public const int kMaxEventsPerHour = 100; // default: 1000
17 public const int kMaxNumberOfElements = 100; // default: 1000
18
19 /// <summary>
20 /// Enumeration type for code authoring APIs mapping to <see cref="InputActionSetupExtensions"/>.
21 /// </summary>
22 /// <remarks>
23 /// This enumeration type may be added to, but NEVER changed, since it would break older data.
24 /// </remarks>
25 public enum Api
26 {
27 AddBinding = 0,
28 AddCompositeBinding = 1,
29 ChangeBinding = 2,
30 ChangeCompositeBinding = 3,
31 Rename = 4,
32 AddControlScheme = 5,
33 RemoveControlScheme = 6,
34 ControlSchemeWithBindingGroup = 7,
35 ControlSchemeWithDevice = 8,
36 ControlSchemeWithRequiredDevice = 9,
37 ControlSchemeWithOptionalDevice = 10,
38 ControlSchemeOrWithRequiredDevice = 11,
39 ControlSchemeOrWithOptionalDevice = 12
40 }
41
42 private static readonly int[] m_Counters = new int[Enum.GetNames(typeof(Api)).Length];
43
44 /// <summary>
45 /// Registers a call to the associated API.
46 /// </summary>
47 /// <param name="api">Enumeration identifying the API.</param>
48 public static void Register(Api api)
49 {
50 if (suppress)
51 return;
52
53 // Note: Currently discards detailed information and only sets a boolean (aggregated) value.
54 ++m_Counters[(int)api];
55 }
56
57 /// <summary>
58 /// Suppresses the registration of analytics.
59 /// </summary>
60 /// <remarks>
61 /// May be used to temporarily suppress analytics to avoid false positives from internal usage.
62 /// </remarks>
63 public static bool suppress
64 {
65 get; set;
66 }
67
68 // Cache delegate
69 private static readonly Action<PlayModeStateChange> PlayModeChanged = OnPlayModeStateChange;
70
71 // Note: Internal visibility to simplify unit testing
72 internal static void OnPlayModeStateChange(PlayModeStateChange change)
73 {
74 if (change == PlayModeStateChange.ExitingEditMode)
75 {
76 // Reset all counters when exiting edit mode
77 Array.Clear(m_Counters, 0, m_Counters.Length);
78
79 // Make sure not suppressed
80 suppress = false;
81 }
82 if (change == PlayModeStateChange.ExitingPlayMode)
83 {
84 // Send analytics and unhook delegate when exiting play-mode
85 EditorApplication.playModeStateChanged -= PlayModeChanged;
86 new InputExitPlayModeAnalytic().Send();
87
88 // No reason to not suppress
89 suppress = true;
90 }
91 }
92
93 [InitializeOnEnterPlayMode]
94 private static void Hook()
95 {
96 // Make sure only a single play-mode change delegate is registered
97 EditorApplication.playModeStateChanged -= PlayModeChanged;
98 EditorApplication.playModeStateChanged += PlayModeChanged;
99 }
100
101 private InputExitPlayModeAnalytic()
102 {
103 info = new InputAnalytics.InputAnalyticInfo(kEventName, kMaxEventsPerHour, kMaxNumberOfElements);
104 }
105
106 /// <summary>
107 /// Represents data collected when exiting play-mode..
108 /// </summary>
109 /// <remarks>
110 /// Ideally this struct should be readonly but then Unity cannot serialize/deserialize it.
111 /// </remarks>
112 [Serializable]
113 public struct Data : UnityEngine.InputSystem.InputAnalytics.IInputAnalyticData
114 {
115 /// <summary>
116 /// Creates a new <c>Data</c> instance.
117 /// </summary>
118 /// <param name="usesCodeAuthoring">Specifies whether code authoring has been used during play-mode.</param>
119 public Data(bool usesCodeAuthoring)
120 {
121 uses_code_authoring = usesCodeAuthoring;
122 }
123
124 /// <summary>
125 /// Specifies whether code-authoring (Input Action setup via extensions) was used at least once during play-mode.
126 /// </summary>
127 public bool uses_code_authoring;
128 }
129
130#if UNITY_2023_2_OR_NEWER
131 public bool TryGatherData(out UnityEngine.Analytics.IAnalytic.IData data, out Exception error)
132#else
133 public bool TryGatherData(out InputAnalytics.IInputAnalyticData data, out Exception error)
134#endif
135 {
136 try
137 {
138 // Determine aggregated perspective, i.e. was "any" code-authoring API used
139 var usedCodeAuthoringDuringPlayMode = false;
140 for (var i = 0; i < m_Counters.Length; ++i)
141 {
142 if (m_Counters[i] > 0)
143 {
144 usedCodeAuthoringDuringPlayMode = true;
145 break;
146 }
147 }
148
149 data = new Data(usedCodeAuthoringDuringPlayMode);
150 error = null;
151 return true;
152 }
153 catch (Exception e)
154 {
155 data = null;
156 error = e;
157 return false;
158 }
159 }
160
161 public InputAnalytics.InputAnalyticInfo info { get; }
162 }
163}
164
165#endif // UNITY_EDITOR