diff --git a/Млаумчерб.Клиент/Network.cs b/Млаумчерб.Клиент/Network.cs index 15f9f0d..47133e1 100644 --- a/Млаумчерб.Клиент/Network.cs +++ b/Млаумчерб.Клиент/Network.cs @@ -1,13 +1,72 @@ -namespace Млаумчерб.Клиент; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; -public record struct NetworkTransferResult +namespace Млаумчерб.Клиент; + +public record struct NetworkTransferResult(long BytesTotal, long BytesTransferred, long BytesPerSecond) { - public long BytesTotal; - public long BytesTransferred; - public long BytesPerSecond; + public override string ToString() + { + return $"transferred {BytesTransferred}/{BytesTotal} bytes ({BytesPerSecond}) bps"; + } } -public class NetworkHelper +public class NetworkTransferTask { + public Task Task { get; private set; } + public Progress Progress { get; private set; } + + private Stream _src; + private Stream _dst; + private CancellationTokenSource _cts; + private DTLib.Timer _timer; + public NetworkTransferTask(Stream src, Stream dst) + { + _src = src; + _dst = dst; + _cts = new CancellationTokenSource(); + Progress = new Progress(); + _timer = new(true, 1000, ReportProgress); + Task = Task.CompletedTask; + } + + public void Start() + { + _timer.Start(); + Task = _src.CopyToAsync(_dst); + _timer.Stop(); + } + + public void Stop() + { + _cts.Cancel(); + _timer.Stop(); + } + + private long previousBytesTransferred; + private void ReportProgress() + { + long bytesTotal = _src.Length, bytesTransferred = _src.Position; + long bytesPerSecond = bytesTransferred - previousBytesTransferred; + previousBytesTransferred = bytesTransferred; + ((IProgress)Progress).Report(new NetworkTransferResult(bytesTotal, bytesTransferred, bytesPerSecond)); + } + + public TaskAwaiter GetAwaiter() => Task.GetAwaiter(); +} + +public static class NetworkHelper +{ + private static HttpClient http = new(); + + public static NetworkTransferTask DownloadHTTPFileAsync(string url, Stream destinationStream) + { + var sourceStream = http.GetStreamAsync(url).GetAwaiter().GetResult(); + NetworkTransferTask task = new(sourceStream, destinationStream); + task.Start(); + return task; + } } \ No newline at end of file diff --git a/Млаумчерб.Клиент/Игра.cs b/Млаумчерб.Клиент/Игра.cs index b21d01a..646470f 100644 --- a/Млаумчерб.Клиент/Игра.cs +++ b/Млаумчерб.Клиент/Игра.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using CliWrap; using DTLib.Extensions; +using Newtonsoft.Json; using Млаумчерб.Клиент.классы; namespace Млаумчерб.Клиент; @@ -11,7 +12,7 @@ public interface IGame string Name { get; } IOPath InstallationDirectory { get; } Progress BeginUpdate(); - void EndUpdate(); + void CancelUpdate(); Task Launch(); void Close(); } @@ -21,17 +22,25 @@ public class MinecraftVersion : IGame public string Name { get; } public IOPath InstallationDirectory { get; } - private IOPath JavawFilePath; + private IOPath JavaExecutableFilePath; + private MinecraftVersionDescriptor descriptor; private JavaArguments javaArgs; private GameArguments gameArgs; private CancellationTokenSource? cts; - private CommandTask commandTask; + private CommandTask? commandTask; - public MinecraftVersion(string name) + public MinecraftVersion(IOPath descriptorFilePath) { - Name = name; - InstallationDirectory = Path.Concat("minecraft", Path.ReplaceRestrictedChars(name)); + Name = descriptorFilePath.LastName().ToString().Replace(".json", ""); + InstallationDirectory = Path.Concat(Приложение.Настройки.путь_к_кубачу, Name); + string descriptorText = File.ReadAllText(descriptorFilePath); + descriptor = JsonConvert.DeserializeObject(descriptorText) + ?? throw new Exception($"can't parse descriptor file '{descriptorFilePath}'"); + javaArgs = new JavaArguments(descriptor); + gameArgs = new GameArguments(descriptor); + JavaExecutableFilePath = Path.Concat(Приложение.Настройки.путь_к_жабе, "bin", + OperatingSystem.IsWindows() ? "javaw.exe" : "javaw"); } public Progress BeginUpdate() @@ -39,7 +48,7 @@ public class MinecraftVersion : IGame throw new NotImplementedException(); } - public void EndUpdate() + public void CancelUpdate() { throw new NotImplementedException(); } @@ -48,7 +57,7 @@ public class MinecraftVersion : IGame { var javaArgsList = javaArgs.FillPlaceholders([]); var gameArgsList = gameArgs.FillPlaceholders([]); - var command = Cli.Wrap(JavawFilePath.ToString()) + var command = Cli.Wrap(JavaExecutableFilePath.ToString()) .WithWorkingDirectory(InstallationDirectory.ToString()) .WithArguments(javaArgsList) .WithArguments(gameArgsList); @@ -68,4 +77,6 @@ public class MinecraftVersion : IGame { cts?.Cancel(); } + + public override string ToString() => Name; } \ No newline at end of file diff --git a/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj b/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj index b37c20e..35d6ae5 100644 --- a/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj +++ b/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj @@ -18,11 +18,8 @@ - - - - - + + diff --git a/Млаумчерб.Клиент/Настройки.cs b/Млаумчерб.Клиент/Настройки.cs index b30f30a..5659b0d 100644 --- a/Млаумчерб.Клиент/Настройки.cs +++ b/Млаумчерб.Клиент/Настройки.cs @@ -7,7 +7,10 @@ public record Настройки public string имя_пользователя { get; set; } = ""; public int выделенная_память_мб { get; set; } = 4096; public bool открывать_на_весь_экран { get; set; } - + public string путь_к_кубачу { get; set; } = "."; + public string путь_к_жабе { get; set; } = "java"; + public bool скачать_жабу { get; set; } = true; + public static Настройки ЗагрузитьИзФайла(string имя_файла = "млаумчерб.настройки") { Приложение.Логгер.LogInfo(nameof(Настройки), $"попытка загрузить настройки из файла '{имя_файла}'"); diff --git a/Млаумчерб.Клиент/Окне.axaml.cs b/Млаумчерб.Клиент/Окне.axaml.cs index 3511493..3424cae 100644 --- a/Млаумчерб.Клиент/Окне.axaml.cs +++ b/Млаумчерб.Клиент/Окне.axaml.cs @@ -57,6 +57,15 @@ public partial class Окне : Window Username = Приложение.Настройки.имя_пользователя; MemoryLimit = Приложение.Настройки.выделенная_память_мб; Fullscreen = Приложение.Настройки.открывать_на_весь_экран; + + IOPath localDescriptorsDir = Path.Concat(Приложение.Настройки.путь_к_кубачу, "version_descriptors"); + Directory.Create(localDescriptorsDir); + var descriptorFiles = Directory.GetFiles(localDescriptorsDir); + foreach(var descriptorFile in descriptorFiles) + { + MinecraftVersion mc = new(descriptorFile); + VersionComboBox.Items.Add(mc); + } } catch (Exception ex) { @@ -72,7 +81,16 @@ public partial class Окне : Window Приложение.Настройки.выделенная_память_мб = MemoryLimit; Приложение.Настройки.открывать_на_весь_экран = Fullscreen; Приложение.Настройки.СохранитьВФайл(); - + + IGame? game = (IGame?)VersionComboBox.SelectionBoxItem; + if (game != null) + { + if (UpdateGameFiles) + { + var progress = game.BeginUpdate(); + progress.ProgressChanged += (o, result) => Приложение.Логгер.LogDebug("Downloads", result); + } + } } catch (Exception ex) {