A game about forced loneliness, made by TACStudios
at master 448 lines 14 kB view raw
1using System; 2using System.IO; 3using System.Linq; 4 5using UnityEditor; 6using UnityEngine; 7 8using Codice.Client.BaseCommands; 9using Codice.Client.Common; 10using Codice.Client.Common.Connection; 11using Codice.Client.Common.Encryption; 12using Codice.Client.Common.EventTracking; 13using Codice.Client.Common.FsNodeReaders; 14using Codice.Client.Common.FsNodeReaders.Watcher; 15using Codice.Client.Common.Threading; 16using Codice.Client.Common.WebApi; 17using Codice.CM.Common; 18using Codice.CM.ConfigureHelper; 19using Codice.CM.WorkspaceServer; 20using Codice.LogWrapper; 21using Codice.Utils; 22using CodiceApp.EventTracking; 23using MacUI; 24using PlasticGui; 25using PlasticPipe.Certificates; 26using Unity.PlasticSCM.Editor.Configuration; 27using Unity.PlasticSCM.Editor.Tool; 28using Unity.PlasticSCM.Editor.UI; 29 30namespace Unity.PlasticSCM.Editor 31{ 32 internal static class PlasticApp 33 { 34 internal static ILog GetLogger(string name) 35 { 36 if (!mIsDomainUnloadHandlerRegistered) 37 { 38 // Register the Domain Unload Handler before the LogManager is initialized, 39 // so the domain unload handler for the app is processed before the log manager one, 40 // and thus the AppDomainUnload is printed in the log 41 RegisterDomainUnloadHandler(); 42 mIsDomainUnloadHandlerRegistered = true; 43 } 44 45 return LogManager.GetLogger(name); 46 } 47 48 internal static bool HasRunningOperation() 49 { 50 if (mWorkspaceWindow != null && 51 mWorkspaceWindow.IsOperationInProgress()) 52 return true; 53 54 if (mWkInfo == null) 55 return false; 56 57 return TransactionManager.Get().ExistsAnyWorkspaceTransaction(mWkInfo); 58 } 59 60 internal static void InitializeIfNeeded() 61 { 62 if (mIsInitialized) 63 return; 64 65 mIsInitialized = true; 66 67 // Configure logging on initialize to avoid adding the performance cost of it 68 // on every Editor load and Domain reload for non-UVCS users. 69 ConfigureLogging(); 70 71 mLog.Debug("InitializeIfNeeded"); 72 73 mLog.DebugFormat("Unity version: {0}", Application.unityVersion); 74 75 // Ensures that the Edition Token is initialized from the UVCS installation regardless of if the PlasticWindow is opened 76 UnityConfigurationChecker.SynchronizeUnityEditionToken(); 77 PlasticInstallPath.LogInstallationInfo(); 78 79 if (!PlasticPlugin.IsUnitTesting) 80 GuiMessage.Initialize(new UnityPlasticGuiMessage()); 81 82 RegisterExceptionHandlers(); 83 RegisterBeforeAssemblyReloadHandler(); 84 RegisterEditorWantsToQuit(); 85 RegisterEditorQuitting(); 86 87 InitLocalization(); 88 89 if (!PlasticPlugin.IsUnitTesting) 90 ThreadWaiter.Initialize(new UnityThreadWaiterBuilder()); 91 92 ServicePointConfigurator.ConfigureServicePoint(); 93 CertificateUi.RegisterHandler(new ChannelCertificateUiImpl()); 94 95 SetupFsWatcher(); 96 97 EditionManager.Get().DisableCapability(EnumEditionCapabilities.Extensions); 98 99 ClientHandlers.Register(); 100 101 PlasticGuiConfig.SetConfigFile(PlasticGuiConfig.UNITY_GUI_CONFIG_FILE); 102 103 if (!PlasticPlugin.IsUnitTesting) 104 { 105 mEventSenderScheduler = EventTracking.Configure( 106 (PlasticWebRestApi)PlasticGui.Plastic.WebRestAPI, 107 ApplicationIdentifier.UnityPackage, 108 IdentifyEventPlatform.Get()); 109 } 110 111 UVCPackageVersion.Initialize(); 112 113 if (mEventSenderScheduler != null) 114 { 115 mPingEventLoop = new PingEventLoop( 116 BuildGetEventExtraInfoFunction.ForPingEvent()); 117 mPingEventLoop.Start(); 118 } 119 120 PlasticMethodExceptionHandling.InitializeAskCredentialsUi( 121 new CredentialsUiImpl()); 122 ClientEncryptionServiceProvider.SetEncryptionPasswordProvider( 123 new MissingEncryptionPasswordPromptHandler()); 124 } 125 126 internal static void Enable() 127 { 128 PlasticGui.Plastic.WebRestAPI.SetToken( 129 CmConnection.Get().BuildWebApiTokenForCloudEditionDefaultUser()); 130 131 if (!PlasticPlugin.IsUnitTesting) 132 SetupCloudOrganizations(PlasticGui.Plastic.WebRestAPI); 133 } 134 135 internal static void SetWorkspace(WorkspaceInfo wkInfo) 136 { 137 mWkInfo = wkInfo; 138 139 RegisterApplicationFocusHandlers(); 140 141 if (mEventSenderScheduler == null) 142 return; 143 144 mPingEventLoop.SetWorkspace(mWkInfo); 145 } 146 147 internal static void RegisterWorkspaceWindow(IWorkspaceWindow workspaceWindow) 148 { 149 mWorkspaceWindow = workspaceWindow; 150 } 151 152 internal static void UnRegisterWorkspaceWindow() 153 { 154 mWorkspaceWindow = null; 155 } 156 157 internal static void EnableMonoFsWatcherIfNeeded() 158 { 159 if (PlatformIdentifier.IsMac()) 160 return; 161 162 MonoFileSystemWatcher.IsEnabled = true; 163 } 164 165 internal static void DisableMonoFsWatcherIfNeeded() 166 { 167 if (PlatformIdentifier.IsMac()) 168 return; 169 170 MonoFileSystemWatcher.IsEnabled = false; 171 } 172 173 internal static void Dispose() 174 { 175 if (!mIsInitialized) 176 return; 177 178 try 179 { 180 mLog.Debug("Dispose"); 181 182 UnRegisterExceptionHandlers(); 183 UnRegisterApplicationFocusHandlers(); 184 UnRegisterEditorWantsToQuit(); 185 UnRegisterEditorQuitting(); 186 187 if (mEventSenderScheduler != null) 188 { 189 mPingEventLoop.Stop(); 190 // Launching and forgetting to avoid a timeout when sending events files and no 191 // network connection is available. 192 // This will be refactored once a better mechanism to send event is in place 193 mEventSenderScheduler.EndAndSendEventsAsync(); 194 } 195 196 if (mWkInfo == null) 197 return; 198 199 WorkspaceFsNodeReaderCachesCleaner.CleanWorkspaceFsNodeReader(mWkInfo); 200 } 201 finally 202 { 203 mIsInitialized = false; 204 } 205 } 206 207 static void SetupCloudOrganizations(IPlasticWebRestApi restApi) 208 { 209 if (!EditionToken.IsCloudEdition()) 210 return; 211 212 // The plastic library holds an internal cache of slugs that relies on the file unityorgs.conf. 213 // This file might contain outdated information or not exist at all, so we need to ensure 214 // the cloud organizations are loaded and populated to the internal cache during the initialization. 215 OrganizationsInformation.LoadCloudOrganizationsAsync(restApi); 216 } 217 218 static void RegisterDomainUnloadHandler() 219 { 220 AppDomain.CurrentDomain.DomainUnload += AppDomainUnload; 221 } 222 223 static void RegisterEditorWantsToQuit() 224 { 225 EditorApplication.wantsToQuit += OnEditorWantsToQuit; 226 } 227 228 static void RegisterEditorQuitting() 229 { 230 EditorApplication.quitting += OnEditorQuitting; 231 } 232 233 static void RegisterBeforeAssemblyReloadHandler() 234 { 235 AssemblyReloadEvents.beforeAssemblyReload += BeforeAssemblyReload; 236 } 237 238 static void RegisterApplicationFocusHandlers() 239 { 240 EditorWindowFocus.OnApplicationActivated += OnApplicationActivated; 241 242 EditorWindowFocus.OnApplicationDeactivated += OnApplicationDeactivated; 243 } 244 245 static void RegisterExceptionHandlers() 246 { 247 AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException; 248 249 Application.logMessageReceivedThreaded += HandleLog; 250 } 251 252 static void UnRegisterDomainUnloadHandler() 253 { 254 AppDomain.CurrentDomain.DomainUnload -= AppDomainUnload; 255 } 256 257 static void UnRegisterEditorWantsToQuit() 258 { 259 EditorApplication.wantsToQuit -= OnEditorWantsToQuit; 260 } 261 262 static void UnRegisterEditorQuitting() 263 { 264 EditorApplication.quitting -= OnEditorQuitting; 265 } 266 267 static void UnRegisterBeforeAssemblyReloadHandler() 268 { 269 AssemblyReloadEvents.beforeAssemblyReload -= BeforeAssemblyReload; 270 } 271 272 static void UnRegisterApplicationFocusHandlers() 273 { 274 EditorWindowFocus.OnApplicationActivated -= OnApplicationActivated; 275 276 EditorWindowFocus.OnApplicationDeactivated -= OnApplicationDeactivated; 277 } 278 279 static void UnRegisterExceptionHandlers() 280 { 281 AppDomain.CurrentDomain.UnhandledException -= HandleUnhandledException; 282 283 Application.logMessageReceivedThreaded -= HandleLog; 284 } 285 286 static void AppDomainUnload(object sender, EventArgs e) 287 { 288 mLog.Debug("AppDomainUnload"); 289 290 UnRegisterDomainUnloadHandler(); 291 } 292 293 static void HandleUnhandledException(object sender, UnhandledExceptionEventArgs args) 294 { 295 Exception ex = (Exception)args.ExceptionObject; 296 297 if (IsExitGUIException(ex) || 298 !IsPlasticStackTrace(ex.StackTrace)) 299 throw ex; 300 301 GUIActionRunner.RunGUIAction(delegate { 302 ExceptionsHandler.HandleException("HandleUnhandledException", ex); 303 }); 304 } 305 306 static void HandleLog(string logString, string stackTrace, LogType type) 307 { 308 if (type != LogType.Exception) 309 return; 310 311 if (!IsPlasticStackTrace(stackTrace)) 312 return; 313 314 GUIActionRunner.RunGUIAction(delegate { 315 mLog.ErrorFormat("[HandleLog] Unexpected error: {0}", logString); 316 mLog.DebugFormat("Stack trace: {0}", stackTrace); 317 318 string message = logString; 319 if (ExceptionsHandler.DumpStackTrace()) 320 message += Environment.NewLine + stackTrace; 321 322 GuiMessage.ShowError(message); 323 }); 324 } 325 326 static void OnApplicationActivated() 327 { 328 mLog.Debug("OnApplicationActivated"); 329 330 EnableMonoFsWatcherIfNeeded(); 331 332 // When the editor gets the focus back, we need to guarantee our status caches are cleared. 333 // This way we can reflect external changes that are not captured by the internal watchers. 334 if (PlasticPlugin.AssetStatusCache != null) 335 { 336 PlasticPlugin.AssetStatusCache.Clear(); 337 } 338 } 339 340 static void OnApplicationDeactivated() 341 { 342 mLog.Debug("OnApplicationDeactivated"); 343 344 DisableMonoFsWatcherIfNeeded(); 345 } 346 347 static void OnEditorQuitting() 348 { 349 mLog.Debug("OnEditorQuitting"); 350 351 PlasticPlugin.Shutdown(); 352 } 353 354 static bool OnEditorWantsToQuit() 355 { 356 mLog.Debug("OnEditorWantsToQuit"); 357 358 if (!HasRunningOperation()) 359 return true; 360 361 return GuiMessage.ShowQuestion( 362 PlasticLocalization.GetString(PlasticLocalization.Name.OperationRunning), 363 PlasticLocalization.GetString(PlasticLocalization.Name.ConfirmClosingRunningOperation), 364 PlasticLocalization.GetString(PlasticLocalization.Name.YesButton)); 365 } 366 367 static void BeforeAssemblyReload() 368 { 369 mLog.Debug("BeforeAssemblyReload"); 370 371 UnRegisterBeforeAssemblyReloadHandler(); 372 373 PlasticShutdown.Shutdown(); 374 } 375 376 static void InitLocalization() 377 { 378 string language = null; 379 try 380 { 381 language = ClientConfig.Get().GetLanguage(); 382 } 383 catch 384 { 385 language = string.Empty; 386 } 387 388 Localization.Init(language); 389 PlasticLocalization.SetLanguage(language); 390 } 391 392 static void SetupFsWatcher() 393 { 394 if (!PlatformIdentifier.IsMac()) 395 return; 396 397 WorkspaceWatcherFsNodeReadersCache.Get().SetMacFsWatcherBuilder( 398 new MacFsWatcherBuilder()); 399 } 400 401 static bool IsPlasticStackTrace(string stackTrace) 402 { 403 if (stackTrace == null) 404 return false; 405 406 string[] namespaces = new[] { 407 "Codice.", 408 "GluonGui.", 409 "PlasticGui." 410 }; 411 412 return namespaces.Any(stackTrace.Contains); 413 } 414 415 static bool IsExitGUIException(Exception ex) 416 { 417 return ex is ExitGUIException; 418 } 419 420 static void ConfigureLogging() 421 { 422 try 423 { 424 string log4netpath = ToolConfig.GetUnityPlasticLogConfigFile(); 425 426 if (!File.Exists(log4netpath)) 427 WriteLogConfiguration.For(log4netpath); 428 429 XmlConfigurator.Configure(new FileInfo(log4netpath)); 430 431 mLog.DebugFormat("Configured logging in '{0}'", log4netpath); 432 } 433 catch 434 { 435 //it failed configuring the logging info; nothing to do. 436 } 437 } 438 439 static bool mIsDomainUnloadHandlerRegistered; 440 441 static bool mIsInitialized; 442 static IWorkspaceWindow mWorkspaceWindow; 443 static WorkspaceInfo mWkInfo; 444 static EventSenderScheduler mEventSenderScheduler; 445 static PingEventLoop mPingEventLoop; 446 static readonly ILog mLog = PlasticApp.GetLogger("PlasticApp"); 447 } 448}