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); } }
private void SafeExecuteOrQueueEx(string name, Action action) { if (myLifetime.IsAlive) { myLocks.ExecuteOrQueueEx(myLifetime, name, action); } }
private void Install(UnityPluginDetector.InstallationInfo installationInfo) { if (!installationInfo.ShouldInstallPlugin) { Assertion.Assert(false, "Should not be here if installation is not required."); return; } if (myPluginInstallations.Contains(mySolution.SolutionFilePath)) { myLogger.Verbose("Installation already done."); return; } myLogger.Info("Installing Rider Unity editor plugin: {0}", installationInfo.InstallReason); if (!TryCopyFiles(installationInfo, out var installedPath)) { myLogger.Warn("Plugin was not installed"); } else { string userTitle; string userMessage; switch (installationInfo.InstallReason) { case UnityPluginDetector.InstallReason.FreshInstall: userTitle = "Unity: plugin installed"; userMessage = $@"Rider plugin v{myCurrentVersion} for the Unity Editor was automatically installed for the project '{mySolution.Name}' and can be found at: {installedPath.MakeRelativeTo(mySolution.SolutionFilePath)}. Please switch back to Unity to load the plugin."; break; case UnityPluginDetector.InstallReason.Update: userTitle = "Unity: plugin updated"; userMessage = $"Editor plugin was successfully updated to version {myCurrentVersion}"; break; case UnityPluginDetector.InstallReason.ForceUpdateForDebug: userTitle = "Unity: plugin updated (debug build)"; userMessage = $"Editor plugin was successfully updated to version {myCurrentVersion}"; break; default: myLogger.Error("Unexpected install reason: {0}", installationInfo.InstallReason); return; } myLogger.Info(userTitle); var notification = new RdNotificationEntry(userTitle, userMessage, true, RdNotificationEntryType.INFO); myShellLocks.ExecuteOrQueueEx(myLifetime, "UnityPluginInstaller.Notify", () => myNotifications.Notification.Fire(notification)); } }
private static void ExecuteActionOnUiThread(IShellLocks shellLocks, string description, Action fOnExecute) { shellLocks.ExecuteOrQueueEx(description, fOnExecute); }
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 Install(UnityPluginDetector.InstallationInfo installationInfo) { if (!installationInfo.ShouldInstallPlugin) { Assertion.Assert(false, "Should not be here if installation is not required."); return; } if (myPluginInstallations.Contains(mySolution.SolutionFilePath)) { myLogger.Verbose("Installation already done."); return; } var currentVersion = typeof(UnityPluginInstaller).Assembly.GetName().Version; if (currentVersion <= installationInfo.Version) { myLogger.Verbose($"Plugin v{installationInfo.Version} already installed."); return; } var isFreshInstall = installationInfo.Version == UnityPluginDetector.ZeroVersion; if (isFreshInstall) { myLogger.Info("Fresh install"); } FileSystemPath installedPath; if (!TryCopyFiles(installationInfo, out installedPath)) { myLogger.Warn("Plugin was not installed"); } else { string userTitle; string userMessage; if (isFreshInstall) { userTitle = "Unity: plugin installed"; userMessage = $@"Rider plugin v{ currentVersion } for the Unity Editor was automatically installed for the project '{mySolution.Name}' This allows better integration between the Unity Editor and Rider IDE. The plugin file can be found on the following path: {installedPath.MakeRelativeTo(mySolution.SolutionFilePath)}. Please switch back to Unity to make plugin file appear in the solution."; } else { userTitle = "Unity: plugin updated"; userMessage = $"Rider plugin was succesfully upgraded to version {currentVersion}"; } myLogger.Info(userTitle); var notification = new RdNotificationEntry(userTitle, userMessage, true, RdNotificationEntryType.INFO); myShellLocks.ExecuteOrQueueEx(myLifetime, "UnityPluginInstaller.Notify", () => myNotifications.Notification.Fire(notification)); } }
private void ResetModel(Lifetime lf, IProtocol protocol) { myUnrealHost.PerformModelAction(riderModel => { UE4Library.RegisterDeclaredTypesSerializers(riderModel.SerializationContext.Serializers); riderModel.EditorId.SetValue(riderModel.EditorId.Value + 1); }); var unrealModel = new RdEditorModel(lf, protocol); UE4Library.RegisterDeclaredTypesSerializers(unrealModel.SerializationContext.Serializers); unrealModel.AllowSetForegroundWindow.Set((lt, pid) => { return(myUnrealHost.PerformModelAction(riderModel => riderModel.AllowSetForegroundWindow.Start(lt, pid)) as RdTask <bool>); }); unrealModel.UnrealLog.Advise(lf, logEvent => { myUnrealHost.PerformModelAction(riderModel => { OnMessageReceived(riderModel, logEvent); }); }); unrealModel.OnBlueprintAdded.Advise(lf, blueprintClass => { //todo }); unrealModel.Play.Advise(lf, val => { myUnrealHost.PerformModelAction(riderModel => { if (PlayedFromRider) { return; } try { PlayedFromUnreal = true; riderModel.Play.Set(val); } finally { PlayedFromUnreal = false; } }); }); unrealModel.PlayMode.Advise(lf, val => { myUnrealHost.PerformModelAction(riderModel => { if (PlayModeFromRider) { return; } try { PlayModeFromUnreal = true; riderModel.PlayMode.Set(val); } finally { PlayModeFromUnreal = false; } }); }); myUnrealHost.PerformModelAction(riderModel => { riderModel.FilterLinkCandidates.Set((lifetime, candidates) => RdTask <ILinkResponse[]> .Successful(candidates .Select(request => myLinkResolver.ResolveLink(request, unrealModel.IsBlueprintPathName)) .AsArray())); riderModel.IsMethodReference.Set((lifetime, methodReference) => { var b = myEditorNavigator.IsMethodReference(methodReference); return(RdTask <bool> .Successful(b)); }); riderModel.OpenBlueprint.Advise(lf, blueprintReference => OnOpenedBlueprint(unrealModel, blueprintReference)); riderModel.NavigateToClass.Advise(lf, uClass => myEditorNavigator.NavigateToClass(uClass)); riderModel.NavigateToMethod.Advise(lf, methodReference => myEditorNavigator.NavigateToMethod(methodReference)); riderModel.Play.Advise(lf, val => { if (PlayedFromUnreal) { return; } try { PlayedFromRider = true; unrealModel.Play.Set(val); } finally { PlayedFromRider = false; } }); riderModel.PlayMode.Advise(lf, val => { if (PlayModeFromUnreal) { return; } try { PlayModeFromRider = true; unrealModel.PlayMode.Set(val); } finally { PlayModeFromRider = false; } }); riderModel.FrameSkip.Advise(lf, skip => unrealModel.FrameSkip.Fire(skip)); }); if (myComponentLifetime.IsAlive) { myLocks.ExecuteOrQueueEx(myComponentLifetime, "setModel", () => { myEditorModel.SetValue(unrealModel); }); } }
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 Install(UnityPluginDetector.InstallationInfo installationInfo, bool force) { if (!force) { if (!installationInfo.ShouldInstallPlugin) { Assertion.Assert(false, "Should not be here if installation is not required."); return; } if (myPluginInstallations.Contains(mySolution.SolutionFilePath)) { myLogger.Verbose("Installation already done."); return; } } myLogger.Info("Installing Rider Unity editor plugin: {0}", installationInfo.InstallReason); if (!TryCopyFiles(installationInfo, out var installedPath)) { myLogger.Warn("Plugin was not installed"); } else { string userTitle; string userMessage; switch (installationInfo.InstallReason) { case UnityPluginDetector.InstallReason.FreshInstall: userTitle = "Unity Editor plugin installed"; userMessage = $@"Please switch to Unity Editor to load the plugin. Rider plugin v{myCurrentVersion} can be found at: {installedPath.MakeRelativeTo(mySolution.SolutionDirectory)}."; break; case UnityPluginDetector.InstallReason.Update: userTitle = "Unity Editor plugin updated"; userMessage = $@"Please switch to the Unity Editor to reload the plugin. Rider plugin v{myCurrentVersion} can be found at: {installedPath.MakeRelativeTo(mySolution.SolutionDirectory)}."; break; case UnityPluginDetector.InstallReason.ForceUpdateForDebug: userTitle = "Unity Editor plugin updated (debug build)"; userMessage = $@"Please switch to the Unity Editor to reload the plugin. Rider plugin v{myCurrentVersion} can be found at: {installedPath.MakeRelativeTo(mySolution.SolutionDirectory)}."; break; case UnityPluginDetector.InstallReason.UpToDate: userTitle = "Unity Editor plugin updated (up to date)"; userMessage = $@"Please switch to the Unity Editor to reload the plugin. Rider plugin v{myCurrentVersion} can be found at: {installedPath.MakeRelativeTo(mySolution.SolutionDirectory)}."; break; default: myLogger.Error("Unexpected install reason: {0}", installationInfo.InstallReason); return; } myLogger.Info(userTitle); var notification = new NotificationModel(userTitle, userMessage, true, RdNotificationEntryType.INFO, new List <NotificationHyperlink>()); myShellLocks.ExecuteOrQueueEx(myLifetime, "UnityPluginInstaller.Notify", () => myNotifications.Notification(notification)); } }
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); } }