Files
NebulaLauncher/Nebula.UpdateResolver/MainWindow.axaml.cs

203 lines
6.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Nebula.UpdateResolver.Configuration;
using Nebula.UpdateResolver.Rest;
namespace Nebula.UpdateResolver;
public partial class MainWindow : Window
{
public static readonly string RootPath = Path.Join(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData), "Datum");
private readonly HttpClient _httpClient = new HttpClient();
public readonly FileApi FileApi = new FileApi(Path.Join(RootPath,"app"));
private string LogStr = "";
public MainWindow()
{
InitializeComponent();
Start();
}
private async Task Start()
{
try
{
var info = await EnsureFiles();
Log("Downloading files...");
foreach (var file in info.ToDelete)
{
Log("Deleting " + file.Path);
FileApi.Remove(file.Path);
}
var loadedManifest = info.FilesExist;
Save(loadedManifest);
var count = info.ToDownload.Count;
var resolved = 0;
foreach (var file in info.ToDownload)
{
using var response = await _httpClient.GetAsync(
ConfigurationStandalone.GetConfigValue(UpdateConVars.UpdateCacheUrl)
+ "/" + file.Hash);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync();
FileApi.Save(file.Path, stream);
resolved++;
Log("Saving " + file.Path, (int)(resolved / (float)count * 100f));
loadedManifest.Add(file);
Save(loadedManifest);
}
Log("Download finished. Running launcher...");
var process = Process.Start(new ProcessStartInfo
{
FileName = "dotnet.exe",
Arguments = Path.Join(FileApi.RootPath, "Nebula.Launcher.dll"),
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.UTF8
});
}
catch(HttpRequestException e){
LogError(e);
Log("Проблемы с интернет-соединением...");
var logPath = Path.Join(RootPath,"updateResloverError.txt");
File.WriteAllText(logPath, LogStr);
Process.Start(new ProcessStartInfo(){
FileName = "notepad",
Arguments = logPath
});
}
catch (Exception e)
{
LogError(e);
var logPath = Path.Join(RootPath,"updateResloverError.txt");
File.WriteAllText(logPath, LogStr);
Process.Start(new ProcessStartInfo(){
FileName = "notepad",
Arguments = logPath
});
}
Thread.Sleep(4000);
Environment.Exit(0);
}
private async Task<ManifestEnsureInfo> EnsureFiles()
{
Log("Ensuring launcher manifest...");
var manifest = await RestStandalone.GetAsync<LauncherManifest>(
new Uri(ConfigurationStandalone.GetConfigValue(UpdateConVars.UpdateCacheUrl)! + "/manifest.json"), CancellationToken.None);
var toDownload = new HashSet<LauncherManifestEntry>();
var toDelete = new HashSet<LauncherManifestEntry>();
var filesExist = new HashSet<LauncherManifestEntry>();
Log("Manifest loaded!");
if (ConfigurationStandalone.TryGetConfigValue(UpdateConVars.CurrentLauncherManifest, out var currentManifest))
{
Log("Delta manifest loaded!");
foreach (var file in currentManifest.Entries)
{
if (!manifest.Entries.Contains(file))
toDelete.Add(EnsurePath(file));
else
filesExist.Add(EnsurePath(file));
}
foreach (var file in manifest.Entries)
{
if(!currentManifest.Entries.Contains(file))
toDownload.Add(EnsurePath(file));
}
}
else
{
toDownload = manifest.Entries;
}
Log("Saving launcher manifest...");
return new ManifestEnsureInfo(toDownload, toDelete, filesExist);
}
private void LogError(Exception e){
Log($"{e.GetType().Name}: "+ e.Message);
Log(e.StackTrace);
if(e.InnerException != null)
LogError(e.InnerException);
}
private void Log(string? message, int percentage = 0)
{
if(message is null) return;
ProgressLabel.Content = message;
if (percentage == 0)
PercentLabel.Content = "";
else
PercentLabel.Content = percentage + "%";
var messageOut = $"[{DateTime.Now.ToUniversalTime():yyyy-MM-dd HH:mm:ss}]: {message} {PercentLabel.Content}";
Console.WriteLine(messageOut);
LogStr += messageOut + "\n";
}
private void Save(HashSet<LauncherManifestEntry> entries)
{
ConfigurationStandalone.SetConfigValue(UpdateConVars.CurrentLauncherManifest, new LauncherManifest(entries));
}
private LauncherManifestEntry EnsurePath(LauncherManifestEntry entry)
{
if(!PathValidator.IsSafePath(FileApi.RootPath, entry.Path))
throw new ArgumentException("Path contains invalid characters. Manifest hash: " + entry.Hash);
return entry;
}
}
public static class PathValidator
{
public static bool IsSafePath(string baseDirectory, string relativePath)
{
if (Path.IsPathRooted(relativePath))
return false;
var fullBase = Path.GetFullPath(baseDirectory);
var combinedPath = Path.Combine(fullBase, relativePath);
var fullPath = Path.GetFullPath(combinedPath);
if (!fullPath.StartsWith(fullBase, StringComparison.Ordinal))
return false;
if (File.Exists(fullPath) || Directory.Exists(fullPath))
{
FileInfo fileInfo = new FileInfo(fullPath);
if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
return false;
}
return true;
}
}