public void UpdateOccurrences( [NotNull] IList <LocalOccurrence> occurrences, [NotNull] IEnumerable <LocalOccurrence> tailOccurrences = null) { lock (mySyncRoot) { myOccurrences = occurrences; mySelectedOccurrence = (occurrences.Count == 0) ? null : occurrences[0]; if (!myShouldDropHighlightings && occurrences.Count == 0) { return; } mySequentialOccurrences.Next(lifetime => { myShellLocks.ExecuteOrQueueReadLock( lifetime, Prefix + "UpdateOccurrence", () => UpdateOccurrencesHighlighting(lifetime, occurrences)); myUpdateSelectedScheduled = true; }); mySequentialFocused.Next(lifetime => { myShellLocks.AssertReadAccessAllowed(); myShellLocks.ExecuteOrQueueReadLock( lifetime, Prefix + "UpdateOccurrence", UpdateFocusedOccurrence); }); } }
public ProductSettingsTracker(Lifetime lifetime, ClientFactory clientFactory, IViewable<ISyncSource> syncSources, IFileSystemTracker fileSystemTracker, JetBoxSettingsStorage jetBoxSettings) { myClientFactory = clientFactory; mySettingsStore = jetBoxSettings.SettingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide); mySettingsStore.Changed.Advise(lifetime, _ => InitClient()); myRootFolder = FileSystemPath.Parse("ReSharperPlatform"); InitClient(); syncSources.View(lifetime, (lt1, source) => source.FilesToSync.View(lt1, (lt2, fileToSync) => { SyncFromCloud(fileToSync.Value); var fileTrackingLifetime = new SequentialLifetimes(lt2); fileToSync.Change.Advise(lt2, args => { var path = args.Property.Value; if (lifetime.IsTerminated || path.IsNullOrEmpty()) { fileTrackingLifetime.TerminateCurrent(); } else { fileTrackingLifetime.Next(lt => fileSystemTracker.AdviseFileChanges(lt, path, delta => delta.Accept(new FileChangesVisitor(myClient, myRootFolder)))); } }); })); }
private void ShowRefactoringAdvice(IHighlighter highlighter, InplaceRefactoringInfo refactoringInfo) { sequentialLifetimes.Next(refactoringLifetime => { var message = GetMessage(); var options = GetOptions(refactoringInfo); agent.ShowBalloon(refactoringLifetime, string.Empty, message, options, new[] { "Cancel" }, false, balloonLifetime => { currentHighlighter = highlighter; agent.ButtonClicked.Advise(balloonLifetime, _ => sequentialLifetimes.TerminateCurrent()); agent.BalloonOptionClicked.Advise(balloonLifetime, tag => { // Terminate first. This kills the balloon window before we start // the refactoring UI. If we start the refactoring UI first, I think // it picks up the balloon window as its parent, and it closes when // the balloon closes sequentialLifetimes.TerminateCurrent(); var action = tag as Action; if (action != null) { action(); } }); }); }); }
private void CreateProtocols(FileSystemPath portFileFullPath) { if (!portFileFullPath.ExistsFile) { return; } var text = File.ReadAllText(portFileFullPath.FullPath); if (!int.TryParse(text, out var port)) { myLogger.Error("Couldn't parse port for from file:{0}, text:{1}", portFileFullPath, text); return; } var modelLifetime = myConnectionLifetimeProducer.Next(); myLogger.Info("Creating SocketWire with port = {0}", port); var wire = new SocketWire.Client(modelLifetime, myDispatcher, port, "UnrealEditorClient"); wire.Connected.Advise(modelLifetime, isConnected => myUnrealHost.PerformModelAction(riderModel => riderModel.IsConnectedToUnrealEditor.SetValue(isConnected))); var protocol = new Protocol("UnrealEditorPlugin", new Serializers(), new Identities(IdKind.Client), myDispatcher, wire, modelLifetime); wire.Connected.WhenTrue(modelLifetime, lifetime => { myLogger.Info("Wire connected"); ResetModel(lifetime, protocol); }); }
public void CreateNew(Lifetime clientLifetime, Action <Lifetime> init) { balloonLifetimes.Next(balloonLifetime => { balloonWindow = new BalloonWindow(); balloonWindow.ButtonClicked += OnButtonClicked; balloonWindow.OptionClicked += OnBalloonOptionClicked; host = new BalloonWindowHost(balloonWindow); // If the client wants to hide the balloon, they can terminate clientLifetime // If another client calls CreateNew, balloonLifetimes.Next terminates // balloonLifetime. Whichever lifetime terminates first will cause // combinedLifetime to terminate, closing the window. var combinedLifetime = Lifetimes.CreateIntersection2(clientLifetime, balloonLifetime).Lifetime; combinedLifetime.AddAction(() => { if (host != null) { host.Close(); host = null; balloonWindow = null; } }); init(combinedLifetime); }); }
public ProductSettingsTracker(Lifetime lifetime, IProductNameAndVersion product, ClientFactory clientFactory, GlobalPerProductStorage globalPerProductStorage, IFileSystemTracker fileSystemTracker, JetBoxSettingsStorage jetBoxSettings) { myClientFactory = clientFactory; mySettingsStore = jetBoxSettings.SettingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide); mySettingsStore.Changed.Advise(lifetime, _ => InitClient()); myRootFolder = FileSystemPath.Parse(product.ProductName); InitClient(); var productSettingsPath = globalPerProductStorage.XmlFileStorage.Path; SyncFromCloud(productSettingsPath.Value); var fileTrackingLifetime = new SequentialLifetimes(lifetime); productSettingsPath.Change.Advise(lifetime, args => { var path = args.Property.Value; if (lifetime.IsTerminated || path.IsNullOrEmpty()) { fileTrackingLifetime.TerminateCurrent(); } else { fileTrackingLifetime.Next(lt => fileSystemTracker.AdviseFileChanges(lt, path, delta => delta.Accept(new FileChangesVisitor(myClient, myRootFolder)))); } }); }
public void TestFailedInNext() { var sequence = new SequentialLifetimes(TestLifetime); var sb = new StringBuilder(); try { sequence.Next(lifetime => { lifetime.Bracket( () => sb.AppendLine($"start"), () => sb.AppendLine($"end")); sb.AppendLine("Before exception"); throw new Exception("Expected"); }); } catch (Exception e) when(e.Message == "Expected") { sb.AppendLine("Expected exception"); } Assert.IsTrue(sequence.IsCurrentTerminated); Assert.AreEqual("start\nBefore exception\nend\nExpected exception\n", sb.ToString().Replace("\r\n", "\n")); }
public ProductSettingsTracker(Lifetime lifetime, ClientFactory clientFactory, IViewable <ISyncSource> syncSources, IFileSystemTracker fileSystemTracker, JetBoxSettingsStorage jetBoxSettings) { myClientFactory = clientFactory; mySettingsStore = jetBoxSettings.SettingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide); mySettingsStore.Changed.Advise(lifetime, _ => InitClient()); myRootFolder = FileSystemPath.Parse("ReSharperPlatform"); InitClient(); syncSources.View(lifetime, (lt1, source) => source.FilesToSync.View(lt1, (lt2, fileToSync) => { SyncFromCloud(fileToSync.Value); var fileTrackingLifetime = new SequentialLifetimes(lt2); fileToSync.Change.Advise(lt2, args => { var path = args.Property.Value; if (lifetime.IsTerminated || path.IsNullOrEmpty()) { fileTrackingLifetime.TerminateCurrent(); } else { fileTrackingLifetime.Next(lt => fileSystemTracker.AdviseFileChanges(lt, path, delta => delta.Accept(new FileChangesVisitor(myClient, myRootFolder)))); } }); })); }
public void ReentrancyPriorityAdviceTest() { using var lifetimeDefinition = new LifetimeDefinition(); var lifetime = lifetimeDefinition.Lifetime; var priorityAdvice = 0; var advice1 = 0; var advice2 = 0; var signal = new Signal <Unit>(); var lifetimes = new SequentialLifetimes(TestLifetime); signal.Advise(lifetime, _ => { advice1++; using (Signal.PriorityAdviseCookie.Create()) { signal.Advise(lifetimes.Next(), _ => priorityAdvice++); } }); signal.Advise(lifetime, _ => advice2++); for (int i = 0; i < 1000; i++) { signal.Fire(); Assert.AreEqual(i + 1, advice1); Assert.AreEqual(i + 1, advice2); Assert.AreEqual(i, priorityAdvice); } }
private void ShowBalloon(string header, string message, IList <BalloonOption> options, params string[] buttons) { lifetimes.Next(balloonLifetime => { var activate = ShowActivated.IsChecked.HasValue && ShowActivated.IsChecked.Value; agent.ShowBalloon(balloonLifetime, header, message, options, buttons, activate, _ => { }); }); }
private void CreateProtocol(FileSystemPath protocolInstancePath, Solution solution) { int port; try { var protocolInstance = JsonConvert.DeserializeObject <ProtocolInstance>(protocolInstancePath.ReadAllText2().Text); port = protocolInstance.port_id; } catch (Exception e) { myLogger.Warn($"Unable to parse {protocolInstancePath}" + Environment.NewLine + e); return; } myLogger.Info($"UNITY_Port {port}."); try { var lifetime = mySessionLifetimes.Next(); myLogger.Info("Create protocol..."); myLogger.Info("Creating SocketWire with port = {0}", port); var wire = new SocketWire.Client(lifetime, myDispatcher, port, "UnityClient"); wire.Connected.WhenTrue(lifetime, lf => { myLogger.Info("WireConnected."); var protocol = new Protocol("UnityEditorPlugin", new Serializers(), new Identities(IdKind.Client), myDispatcher, wire); var model = new UnityModel(lf, protocol); model.IsBackendConnected.Set(rdVoid => true); model.RiderProcessId.SetValue(Process.GetCurrentProcess().Id); solution.SetCustomData("UNITY_SessionInitialized", "true"); SubscribeToLogs(lf, model, solution); SubscribeToOpenFile(model, solution); model.Play.AdviseNotNull(lf, b => solution.SetCustomData("UNITY_Play", b.ToString().ToLower())); model.Pause.AdviseNotNull(lf, b => solution.SetCustomData("UNITY_Pause", b.ToString().ToLower())); myLocks.ExecuteOrQueueEx(myLifetime, "setModel", () => { myUnityModel.SetValue(model, myReadonlyToken); }); lf.AddAction(() => { myLocks.ExecuteOrQueueEx(myLifetime, "clearModel", () => { myLogger.Info("Wire disconnected."); solution.SetCustomData("UNITY_SessionInitialized", "false"); myUnityModel.SetValue(null, myReadonlyToken); }); }); }); } catch (Exception ex) { myLogger.Error(ex); } }
public void TestTerminateCurrent02() { var sb = new StringBuilder(); var sequentialLifetimes = new SequentialLifetimes(TestLifetime); sequentialLifetimes.Next(lifetime => lifetime.OnTermination(() => { sb.Append("T1"); Assert.IsTrue(lifetime.IsNotAlive, "lifetime.IsNotAlive"); Assert.IsTrue(sequentialLifetimes.IsCurrentTerminated, "sequentialLifetimes.IsCurrentTerminated"); })); sequentialLifetimes.Next(lifetime => { sb.Append("N2"); }); Assert.AreEqual("T1N2", sb.ToString()); }
public TodoItemsCountProvider( Lifetime lifetime, IPrimaryTodoManager primaryTodoManager, SolutionSettingsCache solutionSettingsCache, TodoItemsCountDefinitionsCachedSettingsReader todoItemsCountDefinitionsCachedSettingsReader, IEnumerable <ITodoItemsCountConsumer> todoItemsCountConsumers, ISettingsStore settingsStore) { _primaryTodoManager = primaryTodoManager; _settingsCache = solutionSettingsCache; _todoItemsCountDefinitionsCachedSettingsReader = todoItemsCountDefinitionsCachedSettingsReader; _todoItemsCountConsumers = todoItemsCountConsumers.ToIReadOnlyList(); _primaryTodoManager.FilesWereUpdated.Advise(lifetime, files => { // Check for invalid changed files, else we'll get "not valid" exceptions in the 'AllItems' access // later (at least as observed during unit test shut down): if (files.WhereNotNull().All(x => x.IsValid())) { UpdateTodoItemsCounts(); } }); var settingsCacheGetDataSequentialLifeTime = new SequentialLifetimes(lifetime); _currentSettingsCacheLifeTime = settingsCacheGetDataSequentialLifeTime.Next(); settingsStore.AdviseChange(lifetime, _todoItemsCountDefinitionsCachedSettingsReader.KeyExposed, () => { // We use the following lifetime to solve the issue that this 'ISettingsStore.AdviseChange()' callback // arrives earlier than the one used in the cache. => The cache has still the old value when accessed // in 'UpdateTodoItemsCounts()'. => Terminate the cache lifetime explicitly. _currentSettingsCacheLifeTime = settingsCacheGetDataSequentialLifeTime.Next(); UpdateTodoItemsCounts(); }); foreach (var consumer in _todoItemsCountConsumers) { consumer.UpdateRequestSignal.Advise(lifetime, () => { UpdateTodoItemsCounts(); }); } // IDEA: Combine the three event sources and execute update in background thread? }
public void TestTerminateCurrent01() { var sequentialLifetimes = new SequentialLifetimes(TestLifetime); sequentialLifetimes.Next(lifetime => lifetime.OnTermination(() => { Assert.IsTrue(lifetime.IsNotAlive, "lifetime.IsNotAlive"); Assert.IsTrue(sequentialLifetimes.IsCurrentTerminated, "sequentialLifetimes.IsCurrentTerminated"); })); sequentialLifetimes.TerminateCurrent(); }
public static void View <T>(this IReadonlyProperty <T> me, Lifetime lifetime, Action <Lifetime, T> handler) { if (!lifetime.IsAlive) { return; } // nested lifetime is needed due to exception that could be thrown // while viewing a property change right at the moment of <param>lifetime</param>'s termination // but before <param>handler</param> gets removed var lf = lifetime == Lifetime.Eternal ? lifetime : Lifetime.Define(lifetime).Lifetime; var seq = new SequentialLifetimes(lf); me.Advise(lf, v => handler(seq.Next(), v)); }
private void SubscribeToSessionLaunch(Lifetime sessionLifetime, IUnitTestSession session) { var sequentialLifetimes = new SequentialLifetimes(sessionLifetime); session.Launch.Change.Advise(sessionLifetime, args => { if (args.HasNew && args.New != null) { sequentialLifetimes.Next(launchLifetime => { SubscribeToLaunchState(launchLifetime, session, args.New.State); }); } }); }
public void TestTerminateInNext() { var sequence = new SequentialLifetimes(TestLifetime); var sb = new StringBuilder(); sequence.Next(lifetime => { lifetime.Bracket( () => sb.AppendLine($"start"), () => sb.AppendLine($"end")); sb.AppendLine("Before terminate"); sequence.TerminateCurrent(); sb.AppendLine("After terminate"); }); Assert.IsTrue(sequence.IsCurrentTerminated); Assert.AreEqual("start\nBefore terminate\nAfter terminate\nend\n", sb.ToString().Replace("\r\n", "\n")); }
public void TestSimple() { var sequence = new SequentialLifetimes(TestLifetime); var sb = new StringBuilder(); var expected = new StringBuilder(); const int max = 3; for (int i = 0; i < max; i++) { sb.AppendLine($"before {i}"); sequence.Next(lifetime => { var c = i; lifetime.Bracket( () => sb.AppendLine($"start {c}"), () => sb.AppendLine($"end {c}")); sb.AppendLine($"in {c}"); }); if (i == 0) { expected.AppendLine($"before {i}"); } expected.AppendLine($"start {i}"); expected.AppendLine($"in {i}"); if (i != max - 1) { expected.AppendLine($"before {i+1}"); expected.AppendLine($"end {i}"); } } Assert.IsFalse(sequence.IsCurrentTerminated); Assert.AreEqual(expected.ToString(), sb.ToString()); sequence.TerminateCurrent(); Assert.IsTrue(sequence.IsCurrentTerminated); expected.AppendLine($"end {max - 1}"); Assert.AreEqual(expected.ToString(), sb.ToString()); }
public void StartServerToClientMessaging() { myServerToClientLifetime.Next(); }
private void Login() { myLifetimes.Next(lifetime => { myClient.UserLogin = null; myClient.GetTokenAsync( login => { var tcpListener = new TcpListener(IPAddress.Loopback, 0); int port; try { tcpListener.Start(); port = ((IPEndPoint)tcpListener.LocalEndpoint).Port; } finally { tcpListener.Stop(); } var callbackUri = string.Format("http://{0}:{1}/", "localhost", port); var server = new HttpListener(); server.Prefixes.Add(callbackUri); server.Start(); lifetime.AddDispose(server); server.BeginGetContext(ar => { myClient.Logger.CatchAsOuterDataError(() => { if (lifetime.IsTerminated) { return; } var context = server.EndGetContext(ar); // Write a response. using (var writer = new StreamWriter(context.Response.OutputStream)) { string response = @"<html> <head> <title>JetBox - OAuth Authentication</title> </head> <body> <h1>Authorization for JetBox</h1> <p>The application has received your response. You can close this window now.</p> <script type='text/javascript'> window.setTimeout(function() { window.open('', '_self', ''); window.close(); }, 100); if (window.opener) { window.opener.checkToken(); } </script> </body> </html>"; writer.WriteLine(response); writer.Flush(); } context.Response.OutputStream.Flush(); context.Response.OutputStream.Close(); context.Response.Close(); GetInfo(); }); }, null); Environment.OpensUri.OpenUri(new Uri(myClient.BuildAuthorizeUrl(callbackUri))); }, myClient.LogException); }); }
public void StartClientToServerMessaging() { myClientToServerLifetime.Next(); }
private void CreateProtocols(FileSystemPath protocolInstancePath) { if (!protocolInstancePath.ExistsFile) { return; } List <ProtocolInstance> protocolInstanceList; try { protocolInstanceList = ProtocolInstance.FromJson(protocolInstancePath.ReadAllText2().Text); } catch (Exception e) { myLogger.Warn($"Unable to parse {protocolInstancePath}" + Environment.NewLine + e); return; } var protocolInstance = protocolInstanceList?.SingleOrDefault(a => a.SolutionName == mySolution.SolutionFilePath.NameWithoutExtension); if (protocolInstance == null) { return; } myLogger.Info($"EditorPlugin protocol port {protocolInstance.Port} for Solution: {protocolInstance.SolutionName}."); try { var lifetime = mySessionLifetimes.Next(); myLogger.Info("Create protocol..."); myLogger.Info("Creating SocketWire with port = {0}", protocolInstance.Port); var wire = new SocketWire.Client(lifetime, myDispatcher, protocolInstance.Port, "UnityClient"); var protocol = new Protocol("UnityEditorPlugin", new Serializers(), new Identities(IdKind.Client), myDispatcher, wire, lifetime); protocol.ThrowErrorOnOutOfSyncModels = false; protocol.OutOfSyncModels.AdviseOnce(lifetime, e => { if (myPluginInstallations.Contains(mySolution.SolutionFilePath)) { return; } myPluginInstallations.Add(mySolution.SolutionFilePath); // avoid displaying Notification multiple times on each AppDomain.Reload in Unity var appVersion = myUnityVersion.GetActualVersionForSolution(); if (appVersion < new Version(2019, 2)) { var entry = myBoundSettingsStore.Schema.GetScalarEntry((UnitySettings s) => s.InstallUnity3DRiderPlugin); var isEnabled = myBoundSettingsStore.GetValueProperty <bool>(lifetime, entry, null).Value; if (!isEnabled) { myHost.PerformModelAction(model => model.OnEditorModelOutOfSync()); } } else { var notification = new NotificationModel("Advanced Unity integration is unavailable", $"Please update External Editor to {myHostProductInfo.VersionMarketingString} in Unity Preferences.", true, RdNotificationEntryType.WARN); mySolution.Locks.ExecuteOrQueue(lifetime, "OutOfSyncModels.Notify", () => myNotificationsModel.Notification(notification)); } }); wire.Connected.WhenTrue(lifetime, lf => { myLogger.Info("WireConnected."); var editor = new EditorPluginModel(lf, protocol); editor.IsBackendConnected.Set(rdVoid => true); if (PlatformUtil.RuntimePlatform == PlatformUtil.Platform.Windows) { var frontendProcess = Process.GetCurrentProcess().GetParent(); // RiderProcessId is not used on non-Windows, but this line gives bad warning in the log if (frontendProcess != null) { editor.RiderProcessId.SetValue(frontendProcess.Id); } } myHost.PerformModelAction(m => m.SessionInitialized.Value = true); SubscribeToLogs(lf, editor); SubscribeToOpenFile(editor); editor.Play.Advise(lf, b => myHost.PerformModelAction(rd => rd.Play.SetValue(b))); editor.Pause.Advise(lf, b => myHost.PerformModelAction(rd => rd.Pause.SetValue(b))); editor.ClearOnPlay.Advise(lf, time => myHost.PerformModelAction(rd => rd.ClearOnPlay(time))); editor.UnityProcessId.View(lf, (_, pid) => myHost.PerformModelAction(t => t.UnityProcessId.Set(pid))); // I have split this into groups, because want to use async api for finding reference and pass them via groups to Unity myHost.PerformModelAction(t => t.ShowFileInUnity.Advise(lf, v => editor.ShowFileInUnity.Fire(v))); myHost.PerformModelAction(t => t.ShowPreferences.Advise(lf, v => { editor.ShowPreferences.Fire(); })); editor.EditorLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.EditorLogPath.SetValue(s))); editor.PlayerLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.PlayerLogPath.SetValue(s))); // Note that these are late-init properties. Once set, they are always set and do not allow nulls. // This means that if/when the Unity <-> Backend protocol closes, they still retain the last value // they had - so the front end will retain the log and application paths of the just-closed editor. // Opening a new editor instance will reconnect and push a new value through to the front end editor.UnityApplicationData.Advise(lifetime, s => myHost.PerformModelAction(a => { var version = UnityVersion.Parse(s.ApplicationVersion); a.UnityApplicationData.SetValue(new UnityApplicationData(s.ApplicationPath, s.ApplicationContentsPath, s.ApplicationVersion, UnityVersion.RequiresRiderPackage(version))); })); editor.ScriptCompilationDuringPlay.Advise(lifetime, s => myHost.PerformModelAction(a => a.ScriptCompilationDuringPlay.Set(ConvertToScriptCompilationEnum(s)))); myHost.PerformModelAction(rd => { rd.GenerateUIElementsSchema.Set((l, u) => editor.GenerateUIElementsSchema.Start(l, u).ToRdTask(l)); }); TrackActivity(editor, lf); if (!myComponentLifetime.IsTerminated) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "setModel", () => { UnityModel.SetValue(editor); }); } lf.AddAction(() => { if (!myComponentLifetime.IsTerminated) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "clearModel", () => { myLogger.Info("Wire disconnected."); myHost.PerformModelAction(m => m.SessionInitialized.Value = false); UnityModel.SetValue(null); }); } }); }); } catch (Exception ex) { myLogger.Error(ex); } }
private void CreateProtocols(FileSystemPath protocolInstancePath) { if (!protocolInstancePath.ExistsFile) { return; } List <ProtocolInstance> protocolInstanceList; try { protocolInstanceList = ProtocolInstance.FromJson(protocolInstancePath.ReadAllText2().Text); } catch (Exception e) { myLogger.Warn($"Unable to parse {protocolInstancePath}" + Environment.NewLine + e); return; } var protocolInstance = protocolInstanceList?.SingleOrDefault(a => a.SolutionName == mySolution.SolutionFilePath.NameWithoutExtension); if (protocolInstance == null) { return; } myLogger.Info($"EditorPlugin protocol port {protocolInstance.Port} for Solution: {protocolInstance.SolutionName}."); try { var lifetime = mySessionLifetimes.Next(); myLogger.Info("Create protocol..."); myLogger.Info("Creating SocketWire with port = {0}", protocolInstance.Port); var wire = new SocketWire.Client(lifetime, myDispatcher, protocolInstance.Port, "UnityClient"); wire.Connected.WhenTrue(lifetime, lf => { myLogger.Info("WireConnected."); var protocol = new Protocol("UnityEditorPlugin", new Serializers(), new Identities(IdKind.Client), myDispatcher, wire); var editor = new EditorPluginModel(lf, protocol); editor.IsBackendConnected.Set(rdVoid => true); if (PlatformUtil.RuntimePlatform == PlatformUtil.Platform.Windows) { var frontendProcess = Process.GetCurrentProcess().GetParent(); // RiderProcessId is not used on non-Windows, but this line gives bad warning in the log if (frontendProcess != null) { editor.RiderProcessId.SetValue(frontendProcess.Id); } } myHost.PerformModelAction(m => m.SessionInitialized.Value = true); SubscribeToLogs(lf, editor); SubscribeToOpenFile(editor); editor.Play.AdviseNotNull(lf, b => myHost.PerformModelAction(rd => rd.Play.SetValue(b))); editor.Pause.AdviseNotNull(lf, b => myHost.PerformModelAction(rd => rd.Pause.SetValue(b))); editor.UnityProcessId.View(lf, (_, pid) => myHost.PerformModelAction(t => t.UnityProcessId.Set(pid))); // I have split this into groups, because want to use async api for finding reference and pass them via groups to Unity myHost.PerformModelAction(t => t.ShowGameObjectOnScene.Advise(lf, v => editor.ShowGameObjectOnScene.Fire(v.ConvertToUnityModel()))); // pass all references to Unity TODO temp workaround, replace with async api myHost.PerformModelAction(t => t.FindUsageResults.Advise(lf, v => editor.FindUsageResults.Fire(v.ConvertToUnityModel()))); editor.EditorLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.EditorLogPath.SetValue(s))); editor.PlayerLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.PlayerLogPath.SetValue(s))); // Note that these are late-init properties. Once set, they are always set and do not allow nulls. // This means that if/when the Unity <-> Backend protocol closes, they still retain the last value // they had - so the front end will retain the log and application paths of the just-closed editor. // Opening a new editor instance will reconnect and push a new value through to the front end editor.ApplicationPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.ApplicationPath.SetValue(s))); editor.ApplicationContentsPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.ApplicationContentsPath.SetValue(s))); editor.NotifyIsRecompileAndContinuePlaying.AdviseOnce(lifetime, s => myHost.PerformModelAction(a => a.NotifyIsRecompileAndContinuePlaying.Fire(s))); BindPluginPathToSettings(lf, editor); TrackActivity(editor, lf); if (!myComponentLifetime.IsTerminated) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "setModel", () => { UnityModel.SetValue(editor); }); } lf.AddAction(() => { if (!myComponentLifetime.IsTerminated) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "clearModel", () => { myLogger.Info("Wire disconnected."); myHost.PerformModelAction(m => m.SessionInitialized.Value = false); UnityModel.SetValue(null); }); } }); }); } catch (Exception ex) { myLogger.Error(ex); } }
private void OnBuildBegin(vsBuildScope scope, vsBuildAction action) { sequentialLifetimes.Next(buildLifetime => agent.Play(buildLifetime, "Processing")); }
private void CreateProtocol(FileSystemPath protocolInstancePath) { var protocolInstance = GetProtocolInstanceData(protocolInstancePath); if (protocolInstance == null) { return; } myLogger.Info($"EditorPlugin protocol port {protocolInstance.Port} for Solution: {protocolInstance.SolutionName}."); if (protocolInstance.ProtocolGuid != ProtocolCompatibility.ProtocolGuid) { OnOutOfSync(myLifetime); myLogger.Info("Avoid attempt to create protocol, incompatible."); return; } try { var thisSessionLifetime = mySessionLifetimes.Next(); myLogger.Info("Create protocol..."); myLogger.Info("Creating SocketWire with port = {0}", protocolInstance.Port); var wire = new SocketWire.Client(thisSessionLifetime, myDispatcher, protocolInstance.Port, "UnityClient") { BackwardsCompatibleWireFormat = true }; var protocol = new Rd.Impl.Protocol("UnityEditorPlugin", new Serializers(thisSessionLifetime, null, null), new Identities(IdKind.Client), myDispatcher, wire, thisSessionLifetime) { ThrowErrorOnOutOfSyncModels = false }; protocol.OutOfSyncModels.AdviseOnce(thisSessionLifetime, _ => OnOutOfSync(thisSessionLifetime)); wire.Connected.WhenTrue(thisSessionLifetime, connectionLifetime => { myLogger.Info("WireConnected."); var backendUnityModel = new BackendUnityModel(connectionLifetime, protocol); SafeExecuteOrQueueEx("setModel", () => myBackendUnityHost.BackendUnityModel.SetValue(backendUnityModel)); connectionLifetime.OnTermination(() => { SafeExecuteOrQueueEx("clearModel", () => { myLogger.Info("Wire disconnected."); // Clear model myBackendUnityHost.BackendUnityModel.SetValue(null); }); }); }); } catch (Exception ex) { myLogger.Error(ex); } }
private void CreateProtocols(FileSystemPath protocolInstancePath) { if (!protocolInstancePath.ExistsFile) { return; } List <ProtocolInstance> protocolInstanceList; try { protocolInstanceList = ProtocolInstance.FromJson(protocolInstancePath.ReadAllText2().Text); } catch (Exception e) { myLogger.Warn($"Unable to parse {protocolInstancePath}" + Environment.NewLine + e); return; } var protocolInstance = protocolInstanceList?.SingleOrDefault(a => a.SolutionName == mySolution.SolutionFilePath.NameWithoutExtension); if (protocolInstance == null) { return; } myLogger.Info($"UNITY_Port {protocolInstance.Port} for Solution: {protocolInstance.SolutionName}."); try { var lifetime = mySessionLifetimes.Next(); myLogger.Info("Create protocol..."); myLogger.Info("Creating SocketWire with port = {0}", protocolInstance.Port); var wire = new SocketWire.Client(lifetime, myDispatcher, protocolInstance.Port, "UnityClient"); wire.Connected.WhenTrue(lifetime, lf => { myLogger.Info("WireConnected."); var protocol = new Protocol("UnityEditorPlugin", new Serializers(), new Identities(IdKind.Client), myDispatcher, wire); var model = new EditorPluginModel(lf, protocol); model.IsBackendConnected.Set(rdVoid => true); var frontendProcess = Process.GetCurrentProcess().GetParent(); if (frontendProcess != null) { model.RiderProcessId.SetValue(frontendProcess.Id); } myHost.SetModelData("UNITY_SessionInitialized", "true"); SubscribeToLogs(lf, model); SubscribeToOpenFile(model); model.Play.AdviseNotNull(lf, b => myHost.PerformModelAction(a => a.Play.SetValue(b))); model.Pause.AdviseNotNull(lf, b => myHost.SetModelData("UNITY_Pause", b.ToString().ToLower())); // Note that these are late-init properties. Once set, they are always set and do not allow nulls. // This means that if/when the Unity <-> Backend protocol closes, they still retain the last value // they had - so the front end will retain the log and application paths of the just-closed editor. // Opening a new editor instance will reconnect and push a new value through to the front end model.EditorLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.EditorLogPath.SetValue(s))); model.PlayerLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.PlayerLogPath.SetValue(s))); model.ApplicationPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.ApplicationPath.SetValue(s))); model.ApplicationContentsPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.ApplicationContentsPath.SetValue(s))); BindPluginPathToSettings(lf, model); TrackActivity(model, lf); if (!myComponentLifetime.IsTerminated) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "setModel", () => { myUnityModel.SetValue(model, myReadonlyToken); }); } lf.AddAction(() => { if (!myComponentLifetime.IsTerminated) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "clearModel", () => { myLogger.Info("Wire disconnected."); myHost.SetModelData("UNITY_SessionInitialized", "false"); myUnityModel.SetValue(null, myReadonlyToken); }); } }); }); } catch (Exception ex) { myLogger.Error(ex); } }