Skip to content

Commit cbdbbec

Browse files
authored
Merge pull request #597 from Reloaded-Project/emergency-net905-patch
Added: Emergency Patch to rollback .NET 9.0.5 to 9.0.4
2 parents 14ddd7f + c9342f1 commit cbdbbec

16 files changed

Lines changed: 202 additions & 272 deletions

File tree

source/Reloaded.Mod.Installer.DependencyInstaller/DependencyInstaller.cs

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ public static class DependencyInstaller
88
/// Gets all URLs to missing runtime files.
99
/// </summary>
1010
/// <param name="reloadedFolderPath">Folder path for an application, library will check every runtimeconfig.json inside this folder.</param>
11-
public static async Task<HashSet<UrlCommandlinePair>> GetMissingRuntimeUrls(string reloadedFolderPath)
11+
public static async Task<HashSet<MissingDependency>> GetMissingDependencies(string reloadedFolderPath)
1212
{
1313
var allFiles = Directory.GetFiles(reloadedFolderPath, "*.*", SearchOption.AllDirectories);
1414
var filesWithRuntimeConfigs = allFiles.Where(x => x.EndsWith(".runtimeconfig.json")).ToArray();
1515

1616
// Now that we have the runtime config files, get all missing deps from them.
17-
var urlSet = new HashSet<UrlCommandlinePair>();
17+
var urlSet = new HashSet<MissingDependency>();
1818
var dotNetCommandLineParams = new string[]
1919
{
2020
"/install",
@@ -30,38 +30,61 @@ public static async Task<HashSet<UrlCommandlinePair>> GetMissingRuntimeUrls(stri
3030
foreach (var runtimeConfig in filesWithRuntimeConfigs)
3131
{
3232
var runtimeOptions = RuntimeOptions.FromFile(runtimeConfig);
33-
var result = resolver.Resolve(runtimeOptions); ;
33+
var result = resolver.Resolve(runtimeOptions);
3434
foreach (var missingDep in result.MissingDependencies)
3535
{
3636
var frameworkDownloader = new FrameworkDownloader(runtimeOptions.GetAllFrameworks()[0].NuGetVersion, missingDep.FrameworkName);
37-
urlSet.Add(new UrlCommandlinePair()
37+
urlSet.Add(new MissingDependency()
3838
{
39-
FriendlyName = $".NET Core {runtimeOptions.GetAllFrameworks()[0].Version} {architecture}",
40-
Url = await frameworkDownloader.GetDownloadUrlAsync(architecture),
41-
Parameters = dotNetCommandLineParams
39+
FriendlyName = $".NET Core {missingDep.Version} {missingDep.FrameworkName} ({architecture})",
40+
Url = await frameworkDownloader.GetDownloadUrlAsync(architecture, Platform.Windows, Format.Executable, false),
41+
Parameters = dotNetCommandLineParams,
42+
NetFrameworkName = missingDep.FrameworkName,
43+
Version = missingDep.Version,
44+
Architecture = architecture
4245
});
4346
}
4447
}
4548
}
4649

50+
// Filter out redundant .NET Runtime when .NET Desktop Runtime exists
51+
var groupedDeps = urlSet
52+
.Where(dep => dep.NetFrameworkName.HasValue)
53+
.GroupBy(dep => new { dep.Version, dep.Architecture });
54+
55+
foreach (var group in groupedDeps)
56+
{
57+
var depsInGroup = group.ToList();
58+
var hasDesktopRuntime = depsInGroup.Any(dep => dep.NetFrameworkName == FrameworkName.WindowsDesktop);
59+
var appRuntimeDeps = depsInGroup.Where(dep => dep.NetFrameworkName == FrameworkName.App).ToList();
60+
61+
if (hasDesktopRuntime && appRuntimeDeps.Any())
62+
{
63+
foreach (var redundantDep in appRuntimeDeps)
64+
urlSet.Remove(redundantDep);
65+
}
66+
}
67+
4768
// Also check for C++ Runtime.
4869
if (!RedistributablePackage.IsInstalled(RedistributablePackageVersion.VC2015to2019x64))
4970
{
50-
urlSet.Add(new UrlCommandlinePair()
71+
urlSet.Add(new MissingDependency()
5172
{
5273
FriendlyName = "Visual C++ Redist x64",
5374
Url = "https://aka.ms/vs/17/release/vc_redist.x64.exe",
54-
Parameters = dotNetCommandLineParams
75+
Parameters = dotNetCommandLineParams,
76+
Architecture = Architecture.Amd64
5577
});
5678
}
5779

5880
if (!RedistributablePackage.IsInstalled(RedistributablePackageVersion.VC2015to2019x86))
5981
{
60-
urlSet.Add(new UrlCommandlinePair()
82+
urlSet.Add(new MissingDependency()
6183
{
6284
FriendlyName = "Visual C++ Redist x86",
6385
Url = "https://aka.ms/vs/17/release/vc_redist.x86.exe",
64-
Parameters = dotNetCommandLineParams
86+
Parameters = dotNetCommandLineParams,
87+
Architecture = Architecture.x86
6588
});
6689
}
6790

@@ -81,7 +104,7 @@ public static async Task CheckAndInstallMissingRuntimesAsync(string folderPath,
81104
{
82105
// Get files with runtime configurations.
83106
setCurrentStepDescription?.Invoke("Searching for Missing Runtimes");
84-
var urlSet = await GetMissingRuntimeUrls(folderPath);
107+
var urlSet = await GetMissingDependencies(folderPath);
85108

86109
await DownloadAndInstallRuntimesAsync(urlSet, downloadDirPath, progress, setCurrentStepDescription, token);
87110
}
@@ -91,21 +114,22 @@ public static async Task CheckAndInstallMissingRuntimesAsync(string folderPath,
91114
/// [Requires Admin Permission]
92115
/// Finds all missing runtimes and silently installs them.
93116
/// </summary>
94-
/// <param name="urlSet">Set of all URLs and commandline arguments for the downloaded files. Obtained form running <see cref="GetMissingRuntimeUrls"/>.</param>
117+
/// <param name="urlSet">Set of all URLs and commandline arguments for the downloaded files. Obtained form running <see cref="GetMissingDependencies"/>.</param>
95118
/// <param name="downloadDirPath">Path to where the temporary runtimes should be downloaded to.</param>
96119
/// <param name="progress">Progress for the installation operation.</param>
97120
/// <param name="setCurrentStepDescription">Shows a description of the current runtime installation step.</param>
98121
/// <param name="token">Token allowing you to cancel the install task.</param>
99-
public static async Task DownloadAndInstallRuntimesAsync(HashSet<UrlCommandlinePair> urlSet, string downloadDirPath, IProgress<double> progress, Action<string> setCurrentStepDescription, CancellationToken token = default)
122+
public static async Task DownloadAndInstallRuntimesAsync(IEnumerable<MissingDependency> urlSet, string downloadDirPath, IProgress<double> progress, Action<string> setCurrentStepDescription, CancellationToken token = default)
100123
{
101124
// Download Runtimes
102-
setCurrentStepDescription?.Invoke("Downloading & Installing Runtimes");
125+
setCurrentStepDescription?.Invoke("Downloading Runtimes...");
103126
var progressSlicer = new ProgressSlicer(progress);
104127
var downloadSlice = progressSlicer.Slice(0.8f);
105128
var installSlice = progressSlicer.Slice(0.2f);
106129

107-
var downloadQueue = new ConcurrentQueue<UrlCommandlinePair>(urlSet);
108-
var installQueue = new ConcurrentQueue<UrlCommandlinePair>();
130+
var downloadQueue = new ConcurrentQueue<MissingDependency>(urlSet);
131+
var numItems = downloadQueue.Count;
132+
var installQueue = new ConcurrentQueue<MissingDependency>();
109133

110134
// Start background download and install.
111135
var downloadTask = DownloadAllRuntimesAsync();
@@ -116,7 +140,7 @@ async Task DownloadAllRuntimesAsync()
116140
var downloadSlicer = new ProgressSlicer(downloadSlice);
117141
int x = 0;
118142

119-
while (downloadQueue.TryDequeue(out UrlCommandlinePair downloadLink))
143+
while (downloadQueue.TryDequeue(out MissingDependency downloadLink))
120144
{
121145
var client = new WebClient();
122146
var downloadProgress = downloadSlicer.Slice(1.0 / queueElements);
@@ -130,8 +154,9 @@ async Task DownloadAllRuntimesAsync()
130154
if (token.IsCancellationRequested)
131155
throw new TaskCanceledException();
132156

133-
installQueue.Enqueue(new UrlCommandlinePair()
157+
installQueue.Enqueue(new MissingDependency()
134158
{
159+
FriendlyName = downloadLink.FriendlyName,
135160
Parameters = downloadLink.Parameters,
136161
Url = filePath
137162
});
@@ -142,12 +167,13 @@ async Task DownloadAllRuntimesAsync()
142167
async Task InstallAllRuntimesAsync()
143168
{
144169
var installSlicer = new ProgressSlicer(installSlice);
145-
var queueElements = urlSet.Count;
170+
var queueElements = numItems;
146171

147172
loopstart:
148-
while (installQueue.TryDequeue(out UrlCommandlinePair runtimeInstallPath))
173+
while (installQueue.TryDequeue(out MissingDependency runtimeInstallPath))
149174
{
150175
var installProgress = installSlicer.Slice(1.0 / queueElements);
176+
setCurrentStepDescription?.Invoke("Installing Runtime: " + runtimeInstallPath.FriendlyName);
151177
ExecuteAsAdmin(runtimeInstallPath.Url, runtimeInstallPath.Parameters);
152178
installProgress.Report(1);
153179
}
@@ -190,21 +216,66 @@ private static void SetArguments(this ProcessStartInfo processStartInfo, IEnumer
190216
}
191217
}
192218

193-
public class UrlCommandlinePair
219+
public class MissingDependency : IEquatable<MissingDependency>
194220
{
195221
/// <summary>
196222
/// [Optional]
197223
/// Friendly name for the installation.
198224
/// </summary>
199-
public string FriendlyName;
225+
public string FriendlyName { get; set; }
200226

201227
/// <summary>
202228
/// URL for the runtime to be downloaded, or for the extracted runtime.
203229
/// </summary>
204-
public string Url;
230+
public string Url { get; set; }
231+
232+
/// <summary>
233+
/// Architecture of the runtime to be downloaded.
234+
/// </summary>
235+
public Architecture Architecture { get; set; }
205236

206237
/// <summary>
207238
/// Commandline parameters to install the runtime components.
208239
/// </summary>
209-
public IEnumerable<string> Parameters;
240+
public IEnumerable<string> Parameters { get; set; }
241+
242+
/// <summary>
243+
/// Name of the .NET Framework, e.g. "Microsoft.WindowsDesktop.App".
244+
/// </summary>
245+
public FrameworkName? NetFrameworkName { get; set; }
246+
247+
/// <summary>
248+
/// Optional version (if known).
249+
/// </summary>
250+
public string Version { get; set; }
251+
252+
// Autogenerated by R#
253+
public bool Equals(MissingDependency other)
254+
{
255+
if (other is null) return false;
256+
if (ReferenceEquals(this, other)) return true;
257+
return FriendlyName == other.FriendlyName && Url == other.Url && Architecture == other.Architecture && Equals(Parameters, other.Parameters) && NetFrameworkName == other.NetFrameworkName && Version == other.Version;
258+
}
259+
260+
public override bool Equals(object obj)
261+
{
262+
if (obj is null) return false;
263+
if (ReferenceEquals(this, obj)) return true;
264+
if (obj.GetType() != GetType()) return false;
265+
return Equals((MissingDependency)obj);
266+
}
267+
268+
public override int GetHashCode()
269+
{
270+
unchecked
271+
{
272+
var hashCode = (FriendlyName != null ? FriendlyName.GetHashCode() : 0);
273+
hashCode = (hashCode * 397) ^ (Url != null ? Url.GetHashCode() : 0);
274+
hashCode = (hashCode * 397) ^ (int)Architecture;
275+
hashCode = (hashCode * 397) ^ (Parameters != null ? Parameters.GetHashCode() : 0);
276+
hashCode = (hashCode * 397) ^ NetFrameworkName.GetHashCode();
277+
hashCode = (hashCode * 397) ^ (Version != null ? Version.GetHashCode() : 0);
278+
return hashCode;
279+
}
280+
}
210281
}

source/Reloaded.Mod.Launcher.Lib/Models/Model/Dialog/MissingDependency.cs

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using Reloaded.Mod.Installer.DependencyInstaller;
2+
using Reloaded.Mod.Installer.DependencyInstaller.IO;
3+
14
namespace Reloaded.Mod.Launcher.Lib.Models.ViewModel.Dialog;
25

36
/// <summary>
@@ -8,12 +11,60 @@ public class MissingCoreDependencyDialogViewModel : ObservableObject
811
/// <summary>
912
/// List of missing dependencies to run Reloaded.
1013
/// </summary>
11-
public ObservableCollection<MissingDependency> Dependencies { get; set; }
14+
public required ObservableCollection<MissingDependency> Dependencies { get; init; }
15+
16+
/// <summary>
17+
/// Progress of the current download operation.
18+
/// Range 0 - 100.
19+
/// </summary>
20+
public double Progress { get; set; }
21+
22+
/// <summary>
23+
/// True if user can press download button, else false.
24+
/// </summary>
25+
public bool CanDownload { get; set; } = true;
26+
27+
/// <summary>
28+
/// The current step being performed during the download/installation process.
29+
/// </summary>
30+
public string CurrentStep { get; set; } = "";
1231

13-
/// <inheritdoc />
14-
public MissingCoreDependencyDialogViewModel(DependencyChecker deps)
32+
/// <summary>
33+
/// Checks current dependencies, returning a new instance of this ViewModel if any dependencies are missing.
34+
/// </summary>
35+
/// <param name="launcherLocation"></param>
36+
public static async Task<MissingCoreDependencyDialogViewModel> CreateAsync(string launcherLocation)
37+
{
38+
var deps = await DependencyInstaller.GetMissingDependencies(launcherLocation);
39+
return new MissingCoreDependencyDialogViewModel()
40+
{
41+
Dependencies = new ObservableCollection<MissingDependency>(deps)
42+
};
43+
}
44+
45+
/// <summary>
46+
/// True if any dependencies are missing.
47+
/// </summary>
48+
public bool MissingDependencies => Dependencies.Count > 0;
49+
50+
/// <summary>
51+
/// Downloads and installs all missing dependencies with progress reporting.
52+
/// </summary>
53+
public async Task<bool> DownloadAndInstallMissingDependenciesAsync()
1554
{
16-
var models = deps.Dependencies.Where(x => !x.Available).Select(x => new MissingDependency(x));
17-
Dependencies = new ObservableCollection<MissingDependency>(models);
55+
CanDownload = false;
56+
try
57+
{
58+
using var tempDownloadDir = new TemporaryFolderAllocation();
59+
await DependencyInstaller.DownloadAndInstallRuntimesAsync(Dependencies, tempDownloadDir.FolderPath,
60+
new Progress<double>(d => Progress = d * 100),
61+
step => CurrentStep = step);
62+
return true;
63+
}
64+
finally
65+
{
66+
CanDownload = true;
67+
CurrentStep = "";
68+
}
1869
}
1970
}

source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/ModLoaderUpdateDialogViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public async Task Update()
9696

9797
private void OnApplySelfUpdate(string newUpdateDir)
9898
{
99-
var updates = Task.Run(() => DependencyInstaller.GetMissingRuntimeUrls(newUpdateDir)).GetAwaiter().GetResult();
99+
var updates = Task.Run(() => DependencyInstaller.GetMissingDependencies(newUpdateDir)).GetAwaiter().GetResult();
100100
if (updates.Count <= 0)
101101
return;
102102

source/Reloaded.Mod.Launcher.Lib/Reloaded.Mod.Launcher.Lib.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@
4343
</Content>
4444
</ItemGroup>
4545

46-
<ItemGroup>
47-
<Folder Include="Models\Model\DownloadModDialog\" />
48-
</ItemGroup>
49-
5046
<Import Project="..\Reloaded.Mod.Shared\Reloaded.Mod.Shared.projitems" Label="Shared" />
5147

5248
</Project>

source/Reloaded.Mod.Launcher.Lib/Setup.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public static async Task SetupApplicationAsync(Action<string> updateText, int mi
3535

3636
updateText(Resources.SplashCreatingDefaultConfig.Get());
3737
backgroundTasks.Add(UpdateDefaultConfig()); // Fire and forget.
38-
CheckForMissingDependencies();
38+
await CheckForMissingDependenciesAsync(); // blocks async only if missing dependencies, so no perf penalty
3939

4040
updateText(Resources.SplashPreparingResources.Get());
4141
backgroundTasks.Add(Task.Run(CheckForUpdatesAsync)); // Fire and forget, we don't want to delay startup time.
@@ -83,15 +83,17 @@ private static void HandleLogsAndCrashdumps()
8383
/// <summary>
8484
/// Checks for missing mod loader dependencies.
8585
/// </summary>
86-
private static void CheckForMissingDependencies()
86+
private static async Task CheckForMissingDependenciesAsync()
8787
{
88-
var deps = new DependencyChecker(IoC.Get<LoaderConfig>(), IntPtr.Size == 8);
89-
if (deps.AllAvailable)
88+
var loaderConfig = IoC.Get<LoaderConfig>();
89+
var vm = await MissingCoreDependencyDialogViewModel.CreateAsync(Path.GetDirectoryName(loaderConfig.LauncherPath)!);
90+
if (!vm.MissingDependencies)
9091
return;
91-
92+
93+
// ReSharper disable once MethodHasAsyncOverload
9294
ActionWrappers.ExecuteWithApplicationDispatcher(() =>
9395
{
94-
Actions.ShowMissingCoreDependencyDialog(new MissingCoreDependencyDialogViewModel(deps));
96+
Actions.ShowMissingCoreDependencyDialog(vm);
9597
});
9698
}
9799

0 commit comments

Comments
 (0)