Skip to content

Commit 1bfbedc

Browse files
committed
[Core] Preparations for .NET 6.0
1 parent 8b22078 commit 1bfbedc

File tree

7 files changed

+306
-1
lines changed

7 files changed

+306
-1
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using System.Threading.Tasks;
6+
using WDE.Module.Attributes;
7+
using WoWDatabaseEditorCore.Services.Processes;
8+
9+
namespace WoWDatabaseEditorCore.Services.DotNetUtils
10+
{
11+
[UniqueProvider]
12+
public interface IDotNetService
13+
{
14+
Task<bool> IsDotNet6Installed();
15+
Uri DownloadDotNet6Link { get; }
16+
}
17+
18+
[AutoRegister]
19+
[SingleInstance]
20+
public class DotNetService : IDotNetService
21+
{
22+
private readonly IProcessService processService;
23+
24+
public DotNetService(IProcessService processService)
25+
{
26+
this.processService = processService;
27+
}
28+
29+
public async Task<bool> IsDotNet6Installed()
30+
{
31+
try
32+
{
33+
var dotnetPath = "dotnet";
34+
var versions = await processService.RunAndGetOutput(dotnetPath, "--list-runtimes", null);
35+
if (versions == null)
36+
return true;
37+
var runtimes = versions.Split('\n');
38+
39+
return runtimes.Any(r => r.StartsWith("Microsoft.NETCore.App 6.") ||
40+
r.StartsWith("Microsoft.NETCore.App 7.") ||
41+
r.StartsWith("Microsoft.NETCore.App 8."));
42+
}
43+
catch (Exception e)
44+
{
45+
Console.WriteLine(e);
46+
return true;
47+
}
48+
}
49+
50+
private Uri GetLink(OSPlatform os, Architecture arch)
51+
{
52+
var stringOs = os == OSPlatform.Windows ? "windows" : (os == OSPlatform.OSX ? "macos" : "linux");
53+
var version = "6.0.0";
54+
return new Uri($"https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-{version}-{stringOs}-{arch.ToString().ToLower()}-installer");
55+
}
56+
57+
public Uri DownloadDotNet6Link
58+
{
59+
get
60+
{
61+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
62+
return new Uri("https://docs.microsoft.com/dotnet/core/install/linux?WT.mc_id=dotnet-35129-website");
63+
64+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
65+
return GetLink(OSPlatform.OSX, RuntimeInformation.OSArchitecture);
66+
67+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
68+
return GetLink(OSPlatform.Windows, RuntimeInformation.OSArchitecture);
69+
70+
throw new Exception($"Your OS is not supported by .net 6.0");
71+
}
72+
}
73+
}
74+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.IO;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using WDE.Module.Attributes;
6+
7+
namespace WoWDatabaseEditorCore.Services.Processes
8+
{
9+
public interface IProcess
10+
{
11+
bool IsRunning { get; }
12+
void Kill();
13+
}
14+
15+
[UniqueProvider]
16+
public interface IProcessService
17+
{
18+
IProcess RunAndForget(string path, string arguments, string? workingDirectory, bool noWindow,
19+
params (string, string)[] envVars);
20+
21+
Task<int> Run(CancellationToken token, string path, string arguments, string? workingDirectory,
22+
Action<string>? onOutput,
23+
Action<string>? onError,
24+
Action<TextWriter>? onInput = null,
25+
params (string, string)[] envVars);
26+
}
27+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using WDE.Module.Attributes;
7+
8+
namespace WoWDatabaseEditorCore.Services.Processes
9+
{
10+
[AutoRegister]
11+
[SingleInstance]
12+
public class ProcessService : IProcessService
13+
{
14+
public class ProcessData : IProcess
15+
{
16+
private readonly Process process;
17+
18+
public ProcessData(Process process)
19+
{
20+
this.process = process;
21+
}
22+
23+
public bool IsRunning => !process.HasExited;
24+
25+
public void Kill()
26+
{
27+
if (IsRunning)
28+
{
29+
process.Kill();
30+
}
31+
}
32+
}
33+
34+
public IProcess RunAndForget(string path, string arguments, string? workingDirectory, bool noWindow,
35+
params (string, string)[] envVars)
36+
{
37+
var startInfo = new ProcessStartInfo(path, arguments);
38+
startInfo.UseShellExecute = false;
39+
startInfo.CreateNoWindow = noWindow;
40+
41+
foreach (var envVar in envVars)
42+
startInfo.Environment.Add(envVar.Item1, envVar.Item2);
43+
44+
if (workingDirectory != null)
45+
startInfo.WorkingDirectory = workingDirectory;
46+
47+
Process process = new Process();
48+
process.StartInfo = startInfo;
49+
if (!process.Start())
50+
throw new Exception("Cannot start " + path);
51+
return new ProcessData(process);
52+
}
53+
54+
public async Task<int> Run(CancellationToken token, string path, string arguments, string? workingDirectory, Action<string>? onOutput,
55+
Action<string>? onError, Action<TextWriter>? onInput,
56+
params (string, string)[] envVars)
57+
{
58+
var startInfo = new ProcessStartInfo(path, arguments);
59+
startInfo.UseShellExecute = false;
60+
if (onOutput != null)
61+
startInfo.RedirectStandardOutput = true;
62+
if (onError != null)
63+
startInfo.RedirectStandardError = true;
64+
if (onInput != null)
65+
startInfo.RedirectStandardInput = true;
66+
startInfo.CreateNoWindow = true;
67+
68+
foreach (var envVar in envVars)
69+
startInfo.Environment.Add(envVar.Item1, envVar.Item2);
70+
71+
if (workingDirectory != null)
72+
startInfo.WorkingDirectory = workingDirectory;
73+
74+
Process process = new Process();
75+
process.StartInfo = startInfo;
76+
process.ErrorDataReceived += (sender, data) =>
77+
{
78+
if (data.Data != null)
79+
{
80+
onError?.Invoke(data.Data);
81+
}
82+
};
83+
process.OutputDataReceived += (sender, data) =>
84+
{
85+
if (data.Data != null)
86+
{
87+
onOutput?.Invoke(data.Data);
88+
}
89+
};
90+
if (!process.Start())
91+
throw new Exception("Cannot start " + path);
92+
93+
if (onOutput != null)
94+
process.BeginOutputReadLine();
95+
96+
if (onError != null)
97+
process.BeginErrorReadLine();
98+
99+
//onInput?.Invoke(process.StandardInput);
100+
101+
try
102+
{
103+
await process.WaitForExitAsync(token);
104+
}
105+
catch (TaskCanceledException)
106+
{
107+
process.Kill();
108+
}
109+
110+
return process.HasExited ? process.ExitCode : -1;
111+
}
112+
}
113+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.Text;
2+
using System.Threading.Tasks;
3+
using Newtonsoft.Json;
4+
5+
namespace WoWDatabaseEditorCore.Services.Processes
6+
{
7+
public static class ProcessServiceExtensions
8+
{
9+
public static async Task<T?> RunAndGetJson<T>(this IProcessService service, string path, string arguments,
10+
string? workingDirectory)
11+
{
12+
var output = await service.RunAndGetOutput(path, arguments, workingDirectory);
13+
if (output == null)
14+
return default;
15+
16+
T? t = JsonConvert.DeserializeObject<T>(output);
17+
return t;
18+
}
19+
20+
public static async Task<string?> RunAndGetOutput(this IProcessService service, string path, string arguments, string? workingDirectory)
21+
{
22+
StringBuilder sb = new();
23+
bool any = false;
24+
await service.Run(default, path, arguments, workingDirectory, s =>
25+
{
26+
sb.AppendLine(s);
27+
any = true;
28+
}, null);
29+
if (any)
30+
return sb.ToString();
31+
return null;
32+
}
33+
34+
public static async Task<string?> RunAndGetOutputAndError(this IProcessService service, string path, string arguments, string? workingDirectory)
35+
{
36+
StringBuilder sb = new();
37+
bool any = false;
38+
await service.Run(default, path, arguments, workingDirectory, s =>
39+
{
40+
sb.Append(s);
41+
any = true;
42+
}, s =>
43+
{
44+
sb.Append(s);
45+
any = true;
46+
});
47+
if (any)
48+
return sb.ToString();
49+
return null;
50+
}
51+
}
52+
}

WoWDatabaseEditor/ViewModels/QuickStartViewModel.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using System.Collections.ObjectModel;
33
using System.Linq;
4+
using System.Threading.Tasks;
45
using System.Windows.Input;
56
using AsyncAwaitBestPractices.MVVM;
67
using Prism.Commands;
@@ -21,6 +22,7 @@
2122
using WDE.MVVM.Observable;
2223
using WoWDatabaseEditorCore.CoreVersion;
2324
using WoWDatabaseEditorCore.Extensions;
25+
using WoWDatabaseEditorCore.Services.DotNetUtils;
2426
using WoWDatabaseEditorCore.Services.Http;
2527
using WoWDatabaseEditorCore.Services.NewItemService;
2628
using WoWDatabaseEditorCore.Services.Statistics;
@@ -32,6 +34,7 @@ public class QuickStartViewModel : ObservableBase, IDocument
3234
private readonly ISolutionItemIconRegistry iconRegistry;
3335
private readonly ISolutionItemNameRegistry nameRegistry;
3436
private readonly IMostRecentlyUsedService mostRecentlyUsedService;
37+
private readonly IDotNetService dotNetService;
3538
private bool showGiveStarBox;
3639
public AboutViewModel AboutViewModel { get; }
3740
public ObservableCollection<NewItemPrototypeInfo> FlatItemPrototypes { get; } = new();
@@ -62,11 +65,14 @@ public QuickStartViewModel(ISolutionItemProvideService solutionItemProvideServic
6265
IStatisticsService statisticsService,
6366
IApplicationReleaseConfiguration applicationReleaseConfiguration,
6467
IUrlOpenService urlOpenService,
68+
IDotNetService dotNetService,
69+
IWindowManager windowManager,
6570
AboutViewModel aboutViewModel)
6671
{
6772
this.iconRegistry = iconRegistry;
6873
this.nameRegistry = nameRegistry;
6974
this.mostRecentlyUsedService = mostRecentlyUsedService;
75+
this.dotNetService = dotNetService;
7076
Wizards.AddRange(wizards.Where(w => w.IsCompatibleWithCore(currentCoreVersion.Current)));
7177
HasWizards = Wizards.Count > 0;
7278
AboutViewModel = aboutViewModel;
@@ -134,13 +140,28 @@ public QuickStartViewModel(ISolutionItemProvideService solutionItemProvideServic
134140
mainThread.Dispatch(ReloadMruList);
135141
}, true));
136142

143+
OpenDotNet6Website = new AsyncAutoCommand(async () =>
144+
{
145+
var url = dotNetService.DownloadDotNet6Link;
146+
windowManager.OpenUrl(url.ToString());
147+
});
148+
149+
CheckDotNet().ListenErrors();
150+
137151
ShowGiveStarBox = statisticsService.RunCounter > 20 &&
138152
!applicationReleaseConfiguration.GetBool("SKIP_STAR_BOX").GetValueOrDefault() &&
139153
!userSettings.Get<QuickStartSettings>().DismissedLeaveStarBox;
140154

141155
ReloadMruList();
142156
}
143157

158+
private async Task CheckDotNet()
159+
{
160+
var isInstalled = await dotNetService.IsDotNet6Installed();
161+
IsDotNet6Installed = isInstalled;
162+
RaisePropertyChanged(nameof(IsDotNet6Installed));
163+
}
164+
144165
private void ReloadMruList()
145166
{
146167
MostRecentlyUsedItems.Clear();
@@ -154,6 +175,8 @@ private void ReloadMruList()
154175
}
155176
}
156177

178+
public bool IsDotNet6Installed { get; private set; } = true;
179+
public AsyncAutoCommand OpenDotNet6Website { get; }
157180
public AsyncAutoCommand<NewItemPrototypeInfo> LoadItemCommand { get; }
158181
public AsyncAutoCommand<IWizardProvider> LoadWizard { get; }
159182
public AsyncAutoCommand<MostRecentlyUsedViewModel> OpenMostRecentlyUsedCommand { get; }

WoWDatabaseEditorCore.Avalonia/Views/QuickStartView.axaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@
8080
</StackPanel>
8181
</Border>
8282

83+
<Border Background="{DynamicResource SlightlyHighlightedBackground}"
84+
CornerRadius="5"
85+
Padding="10"
86+
Margin="0,10"
87+
BorderThickness="1"
88+
IsVisible="{Binding IsDotNet6Installed, Converter={StaticResource InversedBoolConverter}}"
89+
BorderBrush="Gray">
90+
<StackPanel Orientation="Vertical" TextBlock.FontSize="13">
91+
<TextBlock FontWeight="Bold" TextWrapping="WrapWithOverflow">You need to install .NET 6</TextBlock>
92+
<TextBlock TextWrapping="WrapWithOverflow">In the near future the editor will require new .NET 6. You need to install it in order to continue using the editor. Why not do it now so that you are prepared?</TextBlock>
93+
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
94+
<Button Command="{Binding OpenDotNet6Website}" Margin="0,0,5,0">Okay 👍! (this will open the .net website)</Button>
95+
</StackPanel>
96+
</StackPanel>
97+
</Border>
98+
8399
<Border Background="{DynamicResource SlightlyHighlightedBackground}"
84100
CornerRadius="5"
85101
Padding="10"

0 commit comments

Comments
 (0)