A game about forced loneliness, made by TACStudios
1#if UNITY_ANALYTICS || UNITY_EDITOR 2using System; 3using UnityEngine.InputSystem.Layouts; 4#if UNITY_EDITOR 5using UnityEngine.InputSystem.Editor; 6#endif // UNITY_EDITOR 7 8////FIXME: apparently shutdown events are not coming through in the analytics backend 9 10namespace UnityEngine.InputSystem 11{ 12 internal static class InputAnalytics 13 { 14 public const string kVendorKey = "unity.input"; 15 16 // Struct similar to AnalyticInfo for simplifying usage. 17 public struct InputAnalyticInfo 18 { 19 public InputAnalyticInfo(string name, int maxEventsPerHour, int maxNumberOfElements) 20 { 21 Name = name; 22 MaxEventsPerHour = maxEventsPerHour; 23 MaxNumberOfElements = maxNumberOfElements; 24 } 25 26 public readonly string Name; 27 public readonly int MaxEventsPerHour; 28 public readonly int MaxNumberOfElements; 29 } 30 31 // Note: Needs to be externalized from interface depending on C# version. 32 public interface IInputAnalyticData 33#if UNITY_EDITOR && UNITY_2023_2_OR_NEWER 34 : UnityEngine.Analytics.IAnalytic.IData 35#endif 36 {} 37 38 // Unity 2023.2+ deprecates legacy interfaces for registering and sending editor analytics and 39 // replaces them with attribute annotations and required interface implementations. 40 // The IInputAnalytic interface have been introduced here to support both variants 41 // of analytics reporting. Notice that a difference is that data is collected lazily as part 42 // of sending the analytics via the framework. 43 public interface IInputAnalytic 44#if UNITY_EDITOR && UNITY_2023_2_OR_NEWER 45 : UnityEngine.Analytics.IAnalytic 46#endif // UNITY_EDITOR && UNITY_2023_2_OR_NEWER 47 { 48 InputAnalyticInfo info { get; } // May be removed when only supporting 2023.2+ versions 49 50#if !UNITY_2023_2_OR_NEWER 51 // Conditionally mimic UnityEngine.Analytics.IAnalytic 52 bool TryGatherData(out IInputAnalyticData data, out Exception error); 53#endif // !UNITY_2023_2_OR_NEWER 54 } 55 56 public static void Initialize(InputManager manager) 57 { 58 Debug.Assert(manager.m_Runtime != null); 59 } 60 61 public static void OnStartup(InputManager manager) 62 { 63 manager.m_Runtime.SendAnalytic(new StartupEventAnalytic(manager)); 64 } 65 66 public static void OnShutdown(InputManager manager) 67 { 68 manager.m_Runtime.SendAnalytic(new ShutdownEventDataAnalytic(manager)); 69 } 70 71 /// <summary> 72 /// Data about what configuration we start up with. 73 /// </summary> 74 /// <remarks> 75 /// Has data about the devices present at startup so that we can know what's being 76 /// used out there. Also has data about devices we couldn't recognize. 77 /// 78 /// Note that we exclude devices that are always present (e.g. keyboard and mouse 79 /// on desktops or touchscreen on phones). 80 /// </remarks> 81 [Serializable] 82 public struct StartupEventData : IInputAnalyticData 83 { 84 public string version; 85 public DeviceInfo[] devices; 86 public DeviceInfo[] unrecognized_devices; 87 88 ////REVIEW: ATM we have no way of retrieving these in the player 89 #if UNITY_EDITOR 90 public bool new_enabled; 91 public bool old_enabled; 92 #endif 93 94 [Serializable] 95 public struct DeviceInfo 96 { 97 public string layout; 98 public string @interface; 99 public string product; 100 public bool native; 101 102 public static DeviceInfo FromDescription(InputDeviceDescription description, bool native = false, string layout = null) 103 { 104 string product; 105 if (!string.IsNullOrEmpty(description.product) && !string.IsNullOrEmpty(description.manufacturer)) 106 product = $"{description.manufacturer} {description.product}"; 107 else if (!string.IsNullOrEmpty(description.product)) 108 product = description.product; 109 else 110 product = description.manufacturer; 111 112 if (string.IsNullOrEmpty(layout)) 113 layout = description.deviceClass; 114 115 return new DeviceInfo 116 { 117 layout = layout, 118 @interface = description.interfaceName, 119 product = product, 120 native = native 121 }; 122 } 123 } 124 } 125 126#if UNITY_EDITOR && UNITY_2023_2_OR_NEWER 127 [UnityEngine.Analytics.AnalyticInfo(eventName: kEventName, maxEventsPerHour: kMaxEventsPerHour, maxNumberOfElements: kMaxNumberOfElements, vendorKey: kVendorKey)] 128#endif // UNITY_EDITOR && UNITY_2023_2_OR_NEWER 129 public struct StartupEventAnalytic : IInputAnalytic 130 { 131 public const string kEventName = "input_startup"; 132 public const int kMaxEventsPerHour = 100; 133 public const int kMaxNumberOfElements = 100; 134 135 private InputManager m_InputManager; 136 137 public StartupEventAnalytic(InputManager manager) 138 { 139 m_InputManager = manager; 140 } 141 142 public InputAnalyticInfo info => new InputAnalyticInfo(kEventName, kMaxEventsPerHour, kMaxNumberOfElements); 143 144#if UNITY_EDITOR && UNITY_2023_2_OR_NEWER 145 public bool TryGatherData(out UnityEngine.Analytics.IAnalytic.IData data, out Exception error) 146#else 147 public bool TryGatherData(out IInputAnalyticData data, out Exception error) 148#endif 149 { 150 try 151 { 152 data = new StartupEventData 153 { 154 version = InputSystem.version.ToString(), 155 devices = CollectRecognizedDevices(m_InputManager), 156 unrecognized_devices = CollectUnrecognizedDevices(m_InputManager), 157#if UNITY_EDITOR 158 new_enabled = EditorPlayerSettingHelpers.newSystemBackendsEnabled, 159 old_enabled = EditorPlayerSettingHelpers.oldSystemBackendsEnabled, 160#endif // UNITY_EDITOR 161 }; 162 error = null; 163 return true; 164 } 165 catch (Exception e) 166 { 167 data = null; 168 error = e; 169 return false; 170 } 171 } 172 173 private static StartupEventData.DeviceInfo[] CollectRecognizedDevices(InputManager manager) 174 { 175 var deviceInfo = new StartupEventData.DeviceInfo[manager.devices.Count]; 176 for (var i = 0; i < manager.devices.Count; ++i) 177 { 178 deviceInfo[i] = StartupEventData.DeviceInfo.FromDescription( 179 manager.devices[i].description, manager.devices[i].native, manager.devices[i].layout); 180 } 181 return deviceInfo; 182 } 183 184 private static StartupEventData.DeviceInfo[] CollectUnrecognizedDevices(InputManager manager) 185 { 186 var n = 0; 187 var deviceInfo = new StartupEventData.DeviceInfo[manager.m_AvailableDeviceCount]; 188 for (var i = 0; i < deviceInfo.Length; ++i) 189 { 190 var deviceId = manager.m_AvailableDevices[i].deviceId; 191 if (manager.TryGetDeviceById(deviceId) != null) 192 continue; 193 194 deviceInfo[n++] = StartupEventData.DeviceInfo.FromDescription( 195 manager.m_AvailableDevices[i].description, manager.m_AvailableDevices[i].isNative); 196 } 197 198 if (deviceInfo.Length > n) 199 Array.Resize(ref deviceInfo, n); 200 201 return deviceInfo; 202 } 203 } 204 205 /// <summary> 206 /// Data about when after startup the user first interacted with the application. 207 /// </summary> 208 [Serializable] 209 public struct FirstUserInteractionEventData : IInputAnalyticData 210 { 211 } 212 213 /// <summary> 214 /// Data about what level of data we pumped through the system throughout its lifetime. 215 /// </summary> 216 [Serializable] 217 public struct ShutdownEventData : IInputAnalyticData 218 { 219 public int max_num_devices; 220 public int max_state_size_in_bytes; 221 public int total_event_bytes; 222 public int total_event_count; 223 public int total_frame_count; 224 public float total_event_processing_time; 225 } 226 227#if (UNITY_EDITOR && UNITY_2023_2_OR_NEWER) 228 [UnityEngine.Analytics.AnalyticInfo(eventName: kEventName, maxEventsPerHour: kMaxEventsPerHour, 229 maxNumberOfElements: kMaxNumberOfElements, vendorKey: kVendorKey)] 230#endif // (UNITY_EDITOR && UNITY_2023_2_OR_NEWER) 231 public readonly struct ShutdownEventDataAnalytic : IInputAnalytic 232 { 233 public const string kEventName = "input_shutdown"; 234 public const int kMaxEventsPerHour = 100; 235 public const int kMaxNumberOfElements = 100; 236 237 private readonly InputManager m_InputManager; 238 239 public ShutdownEventDataAnalytic(InputManager manager) 240 { 241 m_InputManager = manager; 242 } 243 244 public InputAnalyticInfo info => new InputAnalyticInfo(kEventName, kMaxEventsPerHour, kMaxNumberOfElements); 245 246#if UNITY_EDITOR && UNITY_2023_2_OR_NEWER 247 public bool TryGatherData(out UnityEngine.Analytics.IAnalytic.IData data, out Exception error) 248#else 249 public bool TryGatherData(out IInputAnalyticData data, out Exception error) 250#endif 251 { 252 try 253 { 254 var metrics = m_InputManager.metrics; 255 data = new ShutdownEventData 256 { 257 max_num_devices = metrics.maxNumDevices, 258 max_state_size_in_bytes = metrics.maxStateSizeInBytes, 259 total_event_bytes = metrics.totalEventBytes, 260 total_event_count = metrics.totalEventCount, 261 total_frame_count = metrics.totalUpdateCount, 262 total_event_processing_time = (float)metrics.totalEventProcessingTime, 263 }; 264 error = null; 265 return true; 266 } 267 catch (Exception e) 268 { 269 data = null; 270 error = e; 271 return false; 272 } 273 } 274 } 275 } 276 277 internal static class AnalyticExtensions 278 { 279 internal static void Send<TSource>(this TSource analytic) where TSource : InputAnalytics.IInputAnalytic 280 { 281 InputSystem.s_Manager?.m_Runtime?.SendAnalytic(analytic); 282 } 283 } 284} 285 286#endif // UNITY_ANALYTICS || UNITY_EDITOR