diff --git a/.gitignore b/.gitignore
index 62faaac..26d6466 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@
.idea/
.editorconfig
*.user
+*.DotSettings
#backups
.old*/
\ No newline at end of file
diff --git a/nuget.config b/nuget.config
deleted file mode 100644
index d9602b1..0000000
--- a/nuget.config
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/Млаумчерб.Клиент/LZMACompressor.cs b/Млаумчерб.Клиент/LZMACompressor.cs
new file mode 100644
index 0000000..123bb34
--- /dev/null
+++ b/Млаумчерб.Клиент/LZMACompressor.cs
@@ -0,0 +1,26 @@
+namespace Млаумчерб.Клиент;
+
+///
+/// https://gist.github.com/ststeiger/cb9750664952f775a341
+///
+public static class LZMACompressor
+{
+ public static void Decompress(Stream inputStream, Stream outputStream)
+ {
+ var decoder = new SevenZip.Compression.LZMA.Decoder();
+ var properties = new byte[5];
+ // Read decoder properties
+ if (inputStream.Read(properties, 0, 5) != 5)
+ throw new Exception("lzma stream is too short");
+ decoder.SetDecoderProperties(properties);
+
+ // Read decompressed data size
+ var fileLengthBytes = new byte[8];
+ if(inputStream.Read(fileLengthBytes, 0, 8) != 8)
+ throw new Exception("lzma stream is too short");
+ long fileLength = BitConverter.ToInt64(fileLengthBytes, 0);
+
+ // Decode
+ decoder.Code(inputStream, outputStream, -1, fileLength, null);
+ }
+}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/Главне.cs b/Млаумчерб.Клиент/Главне.cs
index e055cec..b395293 100644
--- a/Млаумчерб.Клиент/Главне.cs
+++ b/Млаумчерб.Клиент/Главне.cs
@@ -6,6 +6,7 @@ global using System.Text;
global using System.Threading;
global using System.Threading.Tasks;
global using Newtonsoft.Json;
+global using DTLib;
global using DTLib.Logging;
global using DTLib.Filesystem;
global using File = DTLib.Filesystem.File;
@@ -13,7 +14,7 @@ global using Directory = DTLib.Filesystem.Directory;
global using Path = DTLib.Filesystem.Path;
using System.Globalization;
using Avalonia;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
namespace Млаумчерб.Клиент;
diff --git a/Млаумчерб.Клиент/Игра.cs b/Млаумчерб.Клиент/Игра.cs
index f11357a..df98a8f 100644
--- a/Млаумчерб.Клиент/Игра.cs
+++ b/Млаумчерб.Клиент/Игра.cs
@@ -1,6 +1,7 @@
-using CliWrap;
+using System.Linq;
+using CliWrap;
using DTLib.Extensions;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
using Млаумчерб.Клиент.классы;
using Млаумчерб.Клиент.сеть;
using Млаумчерб.Клиент.сеть.NetworkTaskFactories;
@@ -25,15 +26,24 @@ public class GameVersion
public static async Task> GetAllVersionsAsync()
{
- var propsList = new List();
- foreach (IOPath f in Directory.GetFiles(GetVersionDescriptorDir()))
+ var propsSet = new HashSet();
+
+ // local descriptors
+ foreach (IOPath f in Directory.GetFiles(GetVersionDescriptorsDir()))
{
string name = GetVersionDescriptorName(f);
- propsList.Add(new GameVersionProps(name, null, f));
+ propsSet.Add(new GameVersionProps(name, null, f));
}
-
- var remoteVersions = await Сеть.GetDownloadableVersions();
- propsList.AddRange(remoteVersions);
+
+ // remote non-duplicating versions
+ foreach (var removeVersion in await Сеть.GetDownloadableVersions())
+ {
+ propsSet.Add(removeVersion);
+ }
+
+ // reverse sort
+ var propsList = propsSet.ToList();
+ propsList.Sort((a, b) => b.CompareTo(a));
return propsList;
}
@@ -44,7 +54,7 @@ public class GameVersion
if (props.RemoteDescriptorUrl is null)
throw new NullReferenceException("can't download game version descriptor '"
+ props.Name + "', because RemoteDescriptorUrl is null");
- await Сеть.DownloadFileHTTP(props.RemoteDescriptorUrl, props.LocalDescriptorPath);
+ await Сеть.DownloadFile(props.RemoteDescriptorUrl, props.LocalDescriptorPath);
}
return new GameVersion(props);
@@ -63,44 +73,44 @@ public class GameVersion
JavaExecutableFilePath = GetJavaExecutablePath(descriptor.javaVersion.component);
}
- public async Task> CreateUpdateTasksAsync(bool checkHashes)
+ public async Task UpdateFiles(bool checkHashes, Action networkTaskCreatedCallback)
{
+ Приложение.Логгер.LogInfo(nameof(GameVersion), $"started updating version {Name}");
+
List taskFactories =
[
new AssetsDownloadTaskFactory(descriptor),
new LibrariesDownloadTaskFactory(descriptor, libraries),
new VersionFileDownloadTaskFactory(descriptor),
];
- /*if(Приложение.Настройки.скачать_жабу)
+ if(Приложение.Настройки.скачивать_жабу)
{
taskFactories.Add(new JavaDownloadTaskFactory(descriptor));
- }*/
+ }
+ //TODO: modpack
/*if (modpack != null)
{
taskFactories.Add(new ModpackDownloadTaskFactory(modpack));
}*/
- List tasks = new();
+ var networkTasks = new List();
for (int i = 0; i < taskFactories.Count; i++)
{
var nt = await taskFactories[i].CreateAsync(checkHashes);
- if (nt != null)
- tasks.Add(nt);
+ if (nt != null)
+ {
+ networkTasks.Add(nt);
+ networkTaskCreatedCallback.Invoke(nt);
+ }
}
- if (tasks.Count == 0)
+ foreach (var nt in networkTasks)
{
- _props.IsDownloaded = true;
+ await nt.StartAsync();
}
- else
- {
- tasks[^1].OnStop += status =>
- {
- if (status == NetworkTask.Status.Completed)
- _props.IsDownloaded = true;
- };
- }
- return tasks;
+
+ _props.IsDownloaded = true;
+ Приложение.Логгер.LogInfo(nameof(GameVersion), $"finished updating version {Name}");
}
public async Task Launch()
diff --git a/Млаумчерб.Клиент/Логи.cs b/Млаумчерб.Клиент/Логи.cs
index ed58b09..87ca487 100644
--- a/Млаумчерб.Клиент/Логи.cs
+++ b/Млаумчерб.Клиент/Логи.cs
@@ -24,7 +24,9 @@ public class LauncherLogger : ILogger
#endif
}
- public delegate void LogHandler(string context, LogSeverity severity, object message, ILogFormat format);
+ public record LogMessage(string context, LogSeverity severity, object message, ILogFormat format);
+
+ public delegate void LogHandler(LogMessage msg);
public event LogHandler? OnLogMessage;
public void Log(string context, LogSeverity severity, object message, ILogFormat format)
@@ -39,7 +41,7 @@ public class LauncherLogger : ILogger
_ => throw new ArgumentOutOfRangeException(nameof(severity), severity, null)
};
if(isEnabled)
- OnLogMessage?.Invoke(context, severity, message, format);
+ OnLogMessage?.Invoke(new(context, severity, message, format));
}
public void Dispose()
diff --git a/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj b/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj
index 581ae2d..4383775 100644
--- a/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj
+++ b/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj
@@ -16,16 +16,16 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
@@ -33,15 +33,15 @@
-
+
VersionItemView.axaml
Code
-
+
Окне.axaml
Code
-
+
Приложение.axaml
Code
diff --git a/Млаумчерб.Клиент/Настройки.cs b/Млаумчерб.Клиент/Настройки.cs
index b302fdf..b38672f 100644
--- a/Млаумчерб.Клиент/Настройки.cs
+++ b/Млаумчерб.Клиент/Настройки.cs
@@ -1,5 +1,4 @@
-using DTLib.Extensions;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
namespace Млаумчерб.Клиент;
@@ -7,12 +6,11 @@ public record Настройки
{
public string имя_пользователя { get; set; } = "";
public int выделенная_память_мб { get; set; } = 4096;
- public bool открывать_на_весь_экран { get; set; }
public string путь_к_кубачу { get; set; } = ".";
- public bool скачать_жабу { get; set; } = true;
+ public bool запускать_полноэкранное { get; set; } = false;
+ public bool скачивать_жабу { get; set; } = true;
public string? последняя_запущенная_версия { get; set; }
-
- [JsonIgnore] private Stream? fileWriteStream;
+ public int максимум_параллельных_загрузок { get; set; } = 16;
public static Настройки ЗагрузитьИзФайла(string имя_файла = "млаумчерб.настройки")
{
@@ -43,10 +41,8 @@ public record Настройки
{
//TODO: file backup and restore
Приложение.Логгер.LogDebug(nameof(Настройки), $"настройки сохраняются в файл '{имя_файла}'");
- fileWriteStream ??= File.OpenWrite(имя_файла);
var текст = JsonConvert.SerializeObject(this, Formatting.Indented);
- fileWriteStream.Seek(0, SeekOrigin.Begin);
- fileWriteStream.FluentWriteString(текст).Flush();
+ File.WriteAllText(имя_файла, текст);
Приложение.Логгер.LogDebug(nameof(Настройки), $"настройки сохранены: {текст}");
}
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/Ошибки.cs b/Млаумчерб.Клиент/Ошибки.cs
index 3bf5e4a..09b19c1 100644
--- a/Млаумчерб.Клиент/Ошибки.cs
+++ b/Млаумчерб.Клиент/Ошибки.cs
@@ -4,7 +4,7 @@ using MsBox.Avalonia;
using MsBox.Avalonia.Dto;
using MsBox.Avalonia.Enums;
using MsBox.Avalonia.Models;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
namespace Млаумчерб.Клиент;
diff --git a/Млаумчерб.Клиент/видимое/DownloadItemView.axaml b/Млаумчерб.Клиент/видимое/DownloadItemView.axaml
deleted file mode 100644
index a5c5088..0000000
--- a/Млаумчерб.Клиент/видимое/DownloadItemView.axaml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
diff --git a/Млаумчерб.Клиент/видимое/DownloadItemView.axaml.cs b/Млаумчерб.Клиент/видимое/DownloadItemView.axaml.cs
deleted file mode 100644
index 39bf4d9..0000000
--- a/Млаумчерб.Клиент/видимое/DownloadItemView.axaml.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Interactivity;
-using Avalonia.Threading;
-using Млаумчерб.Клиент.сеть;
-
-namespace Млаумчерб.Клиент.видимое;
-
-public partial class DownloadTaskView : UserControl
-{
- private readonly NetworkTask _task;
- private readonly Action _removeFromList;
-
-
- public DownloadTaskView()
- {
- throw new NotImplementedException();
- }
-
- public DownloadTaskView(NetworkTask task, Action removeFromList)
- {
- _task = task;
- _removeFromList = removeFromList;
- InitializeComponent();
- NameText.Text = task.Name;
- task.OnProgress += ReportProgress;
- }
-
-
- void ReportProgress(DownloadProgress progress)
- {
- Dispatcher.UIThread.Invoke(() =>
- {
- DownloadedProgressText.Text = progress.ToString();
- });
- }
-
- private void RemoveFromList(object? sender, RoutedEventArgs e)
- {
- _task.Cancel();
- Dispatcher.UIThread.Invoke(() => _removeFromList.Invoke(this));
- }
-}
diff --git a/Млаумчерб.Клиент/зримое/DownloadItemView.axaml b/Млаумчерб.Клиент/зримое/DownloadItemView.axaml
new file mode 100644
index 0000000..71ee5e5
--- /dev/null
+++ b/Млаумчерб.Клиент/зримое/DownloadItemView.axaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/Млаумчерб.Клиент/зримое/DownloadItemView.axaml.cs b/Млаумчерб.Клиент/зримое/DownloadItemView.axaml.cs
new file mode 100644
index 0000000..e0abe11
--- /dev/null
+++ b/Млаумчерб.Клиент/зримое/DownloadItemView.axaml.cs
@@ -0,0 +1,66 @@
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Threading;
+using Млаумчерб.Клиент.сеть;
+
+namespace Млаумчерб.Клиент.зримое;
+
+public partial class NetworkTaskView : UserControl
+{
+ public readonly NetworkTask Task;
+ private readonly Action _removeFromList;
+
+
+ public NetworkTaskView()
+ {
+ throw new Exception();
+ }
+
+ public NetworkTaskView(NetworkTask task, Action removeFromList)
+ {
+ Task = task;
+ _removeFromList = removeFromList;
+ InitializeComponent();
+ NameText.Text = task.Name;
+ StatusText.Text = task.DownloadStatus.ToString();
+ task.OnStart += OnTaskOnStart;
+ task.OnProgress += ReportProgress;
+ task.OnStop += OnTaskStop;
+ }
+
+ private void OnTaskOnStart()
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ StatusText.Text = Task.DownloadStatus.ToString();
+ });
+ }
+
+ private void OnTaskStop(NetworkTask.Status status)
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ StatusText.Text = status.ToString();
+ if(!string.IsNullOrEmpty(ProgressText.Text))
+ {
+ int speedIndex = ProgressText.Text.IndexOf('(');
+ if(speedIndex > 0)
+ ProgressText.Text = ProgressText.Text.Remove(speedIndex);
+ }
+ });
+ }
+
+ void ReportProgress(DownloadProgress progress)
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ ProgressText.Text = progress.ToString();
+ });
+ }
+
+ private void RemoveFromList(object? sender, RoutedEventArgs e)
+ {
+ Task.Cancel();
+ Dispatcher.UIThread.Invoke(() => _removeFromList.Invoke(this));
+ }
+}
diff --git a/Млаумчерб.Клиент/зримое/LogMessageView.axaml b/Млаумчерб.Клиент/зримое/LogMessageView.axaml
new file mode 100644
index 0000000..a5c3762
--- /dev/null
+++ b/Млаумчерб.Клиент/зримое/LogMessageView.axaml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/Млаумчерб.Клиент/зримое/LogMessageView.axaml.cs b/Млаумчерб.Клиент/зримое/LogMessageView.axaml.cs
new file mode 100644
index 0000000..73c5259
--- /dev/null
+++ b/Млаумчерб.Клиент/зримое/LogMessageView.axaml.cs
@@ -0,0 +1,24 @@
+using Avalonia.Controls;
+using DTLib;
+
+namespace Млаумчерб.Клиент.зримое;
+
+public partial class LogMessageView : UserControl
+{
+ public static ILogFormat ShortLogFormat = new DefaultLogFormat
+ {
+ TimeStampFormat = MyTimeFormat.TimeOnly,
+ PrintContext = false
+ };
+
+ public LogMessageView()
+ {
+ throw new Exception();
+ }
+
+ public LogMessageView(LauncherLogger.LogMessage m)
+ {
+ InitializeComponent();
+ ContentTextBox.Text = ShortLogFormat.CreateMessage(m.context, m.severity, m.message);
+ }
+}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/видимое/VersionItemView.axaml b/Млаумчерб.Клиент/зримое/VersionItemView.axaml
similarity index 83%
rename from Млаумчерб.Клиент/видимое/VersionItemView.axaml
rename to Млаумчерб.Клиент/зримое/VersionItemView.axaml
index 42bfc27..2a629dc 100644
--- a/Млаумчерб.Клиент/видимое/VersionItemView.axaml
+++ b/Млаумчерб.Клиент/зримое/VersionItemView.axaml
@@ -4,6 +4,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Млаумчерб.Клиент"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Млаумчерб.Клиент.видимое.VersionItemView">
+ x:Class="Млаумчерб.Клиент.зримое.VersionItemView">
diff --git a/Млаумчерб.Клиент/видимое/VersionItemView.axaml.cs b/Млаумчерб.Клиент/зримое/VersionItemView.axaml.cs
similarity index 90%
rename from Млаумчерб.Клиент/видимое/VersionItemView.axaml.cs
rename to Млаумчерб.Клиент/зримое/VersionItemView.axaml.cs
index 3aa36be..6ec7da5 100644
--- a/Млаумчерб.Клиент/видимое/VersionItemView.axaml.cs
+++ b/Млаумчерб.Клиент/зримое/VersionItemView.axaml.cs
@@ -2,7 +2,7 @@
using Avalonia.Media;
using Млаумчерб.Клиент.классы;
-namespace Млаумчерб.Клиент.видимое;
+namespace Млаумчерб.Клиент.зримое;
public partial class VersionItemView : ListBoxItem
{
diff --git a/Млаумчерб.Клиент/видимое/Окне.axaml b/Млаумчерб.Клиент/зримое/Окне.axaml
similarity index 64%
rename from Млаумчерб.Клиент/видимое/Окне.axaml
rename to Млаумчерб.Клиент/зримое/Окне.axaml
index 6b5f528..7ac5843 100644
--- a/Млаумчерб.Клиент/видимое/Окне.axaml
+++ b/Млаумчерб.Клиент/зримое/Окне.axaml
@@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gif="clr-namespace:Avalonia.Labs.Gif;assembly=Avalonia.Labs.Gif"
xmlns:local="clr-namespace:Млаумчерб"
- x:Class="Млаумчерб.Клиент.видимое.Окне"
+ x:Class="Млаумчерб.Клиент.зримое.Окне"
Name="window"
Title="млаумчерб"
Icon="avares://млаумчерб/капитал/кубе.ico"
@@ -17,29 +17,8 @@
* 300 *
-
-
- 30 *
-
-
- Лог
-
-
-
-
-
-
-
+
+
@@ -67,14 +46,17 @@
- Запустить полноэкранное
+ Запускать полноэкранное
-
- Проверить файлы игры
+ Проверять файлы игры
+
+
+ Скачивать джаву
-
+
+
+
+ 36 *
+
+
+
+ Лог
+
+
+
+
+
+
+
+
+
+
+
- 30 *
+ 36 *
-
- Загрузки
-
+
+
+ Загрузки
+
+
+
-
+
@@ -106,7 +126,7 @@
-
+
diff --git a/Млаумчерб.Клиент/видимое/Окне.axaml.cs b/Млаумчерб.Клиент/зримое/Окне.axaml.cs
similarity index 63%
rename from Млаумчерб.Клиент/видимое/Окне.axaml.cs
rename to Млаумчерб.Клиент/зримое/Окне.axaml.cs
index e4dc2cb..f139f10 100644
--- a/Млаумчерб.Клиент/видимое/Окне.axaml.cs
+++ b/Млаумчерб.Клиент/зримое/Окне.axaml.cs
@@ -6,7 +6,7 @@ using Avalonia.Platform.Storage;
using Avalonia.Threading;
using Млаумчерб.Клиент.классы;
-namespace Млаумчерб.Клиент.видимое;
+namespace Млаумчерб.Клиент.зримое;
public partial class Окне : Window
{
@@ -46,6 +46,16 @@ public partial class Окне : Window
set => SetValue(CheckGameFilesProperty, value);
}
+
+ public static readonly StyledProperty EnableJavaDownloadProperty =
+ AvaloniaProperty.Register<Окне, bool>(nameof(EnableJavaDownload),
+ defaultBindingMode: BindingMode.TwoWay, defaultValue: true);
+ public bool EnableJavaDownload
+ {
+ get => GetValue(EnableJavaDownloadProperty);
+ set => SetValue(EnableJavaDownloadProperty, value);
+ }
+
public Окне()
{
InitializeComponent();
@@ -55,34 +65,14 @@ public partial class Окне : Window
{
try
{
- Приложение.Логгер.OnLogMessage += (context, severity, message, format) =>
- {
- if(severity == LogSeverity.Debug)
- return;
-
- StringBuilder b = new();
- b.Append(DateTime.Now.ToString("[HH:mm:ss]["));
- b.Append(severity);
- b.Append("]: ");
- b.Append(message);
- b.Append('\n');
- Dispatcher.UIThread.Invoke(() =>
- {
- double offsetFromBottom = LogScrollViewer.Extent.Height
- - LogScrollViewer.Offset.Y
- - LogScrollViewer.Viewport.Height;
- bool is_scrolled_to_end = offsetFromBottom < 20.0; // scrolled less then one line up
- LogTextBox.Text += b.ToString();
- if (is_scrolled_to_end)
- LogScrollViewer.ScrollToEnd();
- });
- };
-
+ Приложение.Логгер.OnLogMessage += GuiLogMessage;
+
Username = Приложение.Настройки.имя_пользователя;
MemoryLimit = Приложение.Настройки.выделенная_память_мб;
- Fullscreen = Приложение.Настройки.открывать_на_весь_экран;
-
- Directory.Create(Пути.GetVersionDescriptorDir());
+ Fullscreen = Приложение.Настройки.запускать_полноэкранное;
+ EnableJavaDownload = Приложение.Настройки.скачивать_жабу;
+
+ Directory.Create(Пути.GetVersionDescriptorsDir());
VersionComboBox.SelectedIndex = 0;
VersionComboBox.IsEnabled = false;
var versions = await GameVersion.GetAllVersionsAsync();
@@ -91,8 +81,8 @@ public partial class Окне : Window
foreach (var p in versions)
{
VersionComboBox.Items.Add(new VersionItemView(p));
- if (Приложение.Настройки.последняя_запущенная_версия != null &&
- p.Name == Приложение.Настройки.последняя_запущенная_версия)
+ if (Приложение.Настройки.последняя_запущенная_версия != null &&
+ p.Name == Приложение.Настройки.последняя_запущенная_версия)
VersionComboBox.SelectedIndex = VersionComboBox.Items.Count - 1;
}
VersionComboBox.IsEnabled = true;
@@ -104,38 +94,62 @@ public partial class Окне : Window
}
}
+ private void GuiLogMessage(LauncherLogger.LogMessage msg)
+ {
+ if (msg.severity == LogSeverity.Debug) return;
+
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ double offsetFromBottom = LogScrollViewer.Extent.Height - LogScrollViewer.Offset.Y - LogScrollViewer.Viewport.Height;
+ bool is_scrolled_to_end = offsetFromBottom < 20.0; // scrolled less then one line up
+ LogPanel.Children.Add(new LogMessageView(msg));
+ if (is_scrolled_to_end) LogScrollViewer.ScrollToEnd();
+ });
+ }
+
private async void Запуск(object? sender, RoutedEventArgs e)
{
try
{
+ Dispatcher.UIThread.Invoke(() => LaunchButton.IsEnabled = false);
+
var selectedVersionView = (VersionItemView?)VersionComboBox.SelectedItem;
var selectedVersion = selectedVersionView?.Props;
Приложение.Настройки.последняя_запущенная_версия = selectedVersion?.Name;
Приложение.Настройки.имя_пользователя = Username;
Приложение.Настройки.выделенная_память_мб = MemoryLimit;
- Приложение.Настройки.открывать_на_весь_экран = Fullscreen;
+ Приложение.Настройки.запускать_полноэкранное = Fullscreen;
+ Приложение.Настройки.скачивать_жабу = EnableJavaDownload;
Приложение.Настройки.СохранитьВФайл();
if (selectedVersion == null)
return;
var v = await GameVersion.CreateFromPropsAsync(selectedVersion);
- var updateTasks = await v.CreateUpdateTasksAsync(CheckGameFiles);
- foreach (var t in updateTasks)
- {
- var updateTask = t.StartAsync();
- Dispatcher.UIThread.Invoke(() =>
+ await v.UpdateFiles(CheckGameFiles, nt =>
{
- var view = new DownloadTaskView(t, view => DownloadsPanel.Children.Remove(view));
- DownloadsPanel.Children.Add(view);
- });
- await updateTask;
- }
- Dispatcher.UIThread.Invoke(() => CheckGameFiles = false);
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ DownloadsPanel.Children.Add(new NetworkTaskView(nt,
+ ntv => DownloadsPanel.Children.Remove(ntv)));
+ });
+ }
+ );
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ CheckGameFiles = false;
+ });
}
catch (Exception ex)
{
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
}
+ finally
+ {
+ Dispatcher.UIThread.Invoke(() =>
+ {
+ LaunchButton.IsEnabled = true;
+ });
+ }
}
private void ОткрытьПапкуЛаунчера(object? s, RoutedEventArgs e)
@@ -176,4 +190,20 @@ public partial class Окне : Window
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
}
}
+
+ private void ClearLogPanel(object? sender, RoutedEventArgs e)
+ {
+ LogPanel.Children.Clear();
+ }
+
+
+ private void ClearDownloadsPanel(object? sender, RoutedEventArgs e)
+ {
+ foreach (var control in DownloadsPanel.Children)
+ {
+ var nt = (NetworkTaskView)control;
+ nt.Task.Cancel();
+ }
+ DownloadsPanel.Children.Clear();
+ }
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/видимое/Приложение.axaml b/Млаумчерб.Клиент/зримое/Приложение.axaml
similarity index 94%
rename from Млаумчерб.Клиент/видимое/Приложение.axaml
rename to Млаумчерб.Клиент/зримое/Приложение.axaml
index 9c71f16..93ed858 100644
--- a/Млаумчерб.Клиент/видимое/Приложение.axaml
+++ b/Млаумчерб.Клиент/зримое/Приложение.axaml
@@ -1,6 +1,6 @@
diff --git a/Млаумчерб.Клиент/видимое/Приложение.axaml.cs b/Млаумчерб.Клиент/зримое/Приложение.axaml.cs
similarity index 90%
rename from Млаумчерб.Клиент/видимое/Приложение.axaml.cs
rename to Млаумчерб.Клиент/зримое/Приложение.axaml.cs
index 744411e..45e96ba 100644
--- a/Млаумчерб.Клиент/видимое/Приложение.axaml.cs
+++ b/Млаумчерб.Клиент/зримое/Приложение.axaml.cs
@@ -2,7 +2,7 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
-namespace Млаумчерб.Клиент.видимое;
+namespace Млаумчерб.Клиент.зримое;
public class Приложение : Application
{
diff --git a/Млаумчерб.Клиент/классы/GameVersionDescriptor.cs b/Млаумчерб.Клиент/классы/GameVersionDescriptor.cs
index 56e83d9..761a592 100644
--- a/Млаумчерб.Клиент/классы/GameVersionDescriptor.cs
+++ b/Млаумчерб.Клиент/классы/GameVersionDescriptor.cs
@@ -11,10 +11,10 @@ public class GameVersionDescriptor
[JsonRequired] public string type { get; set; } = "";
[JsonRequired] public string mainClass { get; set; } = "";
[JsonRequired] public Downloads downloads { get; set; } = null!;
- [JsonRequired] public JavaVersion javaVersion { get; set; } = null!;
[JsonRequired] public List libraries { get; set; } = null!;
[JsonRequired] public AssetIndexProperties assetIndex { get; set; } = null!;
[JsonRequired] public string assets { get; set; } = "";
+ public JavaVersion javaVersion { get; set; } = new() { component = "jre-legacy", majorVersion = 8 };
public string? minecraftArguments { get; set; }
public ArgumentsNew? arguments { get; set; }
}
diff --git a/Млаумчерб.Клиент/классы/GameVersionProps.cs b/Млаумчерб.Клиент/классы/GameVersionProps.cs
index a508f16..e9e1494 100644
--- a/Млаумчерб.Клиент/классы/GameVersionProps.cs
+++ b/Млаумчерб.Клиент/классы/GameVersionProps.cs
@@ -1,6 +1,6 @@
namespace Млаумчерб.Клиент.классы;
-public class GameVersionProps
+public class GameVersionProps : IComparable, IEquatable
{
public string Name { get; }
public IOPath LocalDescriptorPath { get; }
@@ -31,4 +31,33 @@ public class GameVersionProps
this(name, url, Пути.GetVersionDescriptorPath(name)) { }
public override string ToString() => Name;
+
+ public override int GetHashCode() => Name.GetHashCode();
+
+ public int CompareTo(GameVersionProps? other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (other is null) return 1;
+
+ if (Version.TryParse(Name, out var version1) && Version.TryParse(other.Name, out var version2))
+ {
+ return version1.CompareTo(version2);
+ }
+
+ return String.Compare(Name, other.Name, StringComparison.InvariantCulture);
+ }
+
+ public bool Equals(GameVersionProps? other)
+ {
+ if (other is null) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Name == other.Name;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is GameVersionProps other) return Equals(other);
+ if (ReferenceEquals(this, obj)) return true;
+ return false;
+ }
}
diff --git a/Млаумчерб.Клиент/классы/JavaVersionCatalog.cs b/Млаумчерб.Клиент/классы/JavaVersionCatalog.cs
index c2c6380..af18f96 100644
--- a/Млаумчерб.Клиент/классы/JavaVersionCatalog.cs
+++ b/Млаумчерб.Клиент/классы/JavaVersionCatalog.cs
@@ -1,23 +1,77 @@
-namespace Млаумчерб.Клиент.классы;
+using System.Runtime.InteropServices;
+
+namespace Млаумчерб.Клиент.классы;
public class JavaVersionCatalog
{
-
+ [JsonProperty("linux")]
+ public Dictionary? linux_x86 { get; set; }
+ [JsonProperty("linux-i386")]
+ public Dictionary? linux_x64 { get; set; }
+ [JsonProperty("mac-os")]
+ public Dictionary? osx_x64 { get; set; }
+ [JsonProperty("mac-os-arm64")]
+ public Dictionary? osx_arm64 { get; set; }
+ [JsonProperty("windows-arm64")]
+ public Dictionary? windows_arm64 { get; set; }
+ [JsonProperty("windows-x64")]
+ public Dictionary? windows_x64 { get; set; }
+ [JsonProperty("windows-x86")]
+ public Dictionary? windows_x86 { get; set; }
+
+ public JavaVersionProps GetVersionProps(JavaVersion version)
+ {
+ var arch = RuntimeInformation.OSArchitecture;
+ Dictionary? propsDict = null;
+ switch (arch)
+ {
+ case Architecture.X86:
+ if (OperatingSystem.IsWindows())
+ propsDict = windows_x86;
+ else if (OperatingSystem.IsLinux())
+ propsDict = linux_x86;
+ break;
+ case Architecture.X64:
+ if (OperatingSystem.IsWindows())
+ propsDict = windows_x64;
+ else if (OperatingSystem.IsLinux())
+ propsDict = linux_x64;
+ else if (OperatingSystem.IsMacOS())
+ propsDict = osx_x64;
+ break;
+ case Architecture.Arm64:
+ if (OperatingSystem.IsWindows())
+ propsDict = windows_arm64;
+ else if (OperatingSystem.IsMacOS())
+ propsDict = osx_arm64;
+ break;
+ }
+
+ if (propsDict != null && propsDict.TryGetValue(version.component, out var props_array))
+ {
+ if (props_array.Length != 0)
+ return props_array[0];
+ }
+
+ throw new PlatformNotSupportedException($"Can't download java {version.majorVersion} for your operating system. " +
+ $"Download it manually to directory {Пути.GetJavaRuntimeDir(version.component)}");
+ }
}
public class JavaVersionProps
{
- [JsonRequired] public Artifact manifest { get; set; }
+ /// url of JavaDistributiveManifest
+ [JsonRequired] public Artifact manifest { get; set; } = null!;
}
-public class JavaVersionManifest
+public class JavaDistributiveManifest
{
- [JsonRequired] public Dictionary manifest { get; set; }
+ [JsonRequired] public Dictionary files { get; set; } = null!;
}
public class JavaDistributiveElementProps
{
- // "directory" / "file"
+ /// "directory" / "file"
[JsonRequired] public string type { get; set; } = "";
public bool? executable { get; set; }
public JavaCompressedArtifact? downloads { get; set; }
@@ -26,5 +80,5 @@ public class JavaDistributiveElementProps
public class JavaCompressedArtifact
{
public Artifact? lzma { get; set; }
- public Artifact raw { get; set; } = null!;
+ [JsonRequired] public Artifact raw { get; set; } = null!;
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/классы/Libraries.cs b/Млаумчерб.Клиент/классы/Libraries.cs
index e2effdd..f7e23ae 100644
--- a/Млаумчерб.Клиент/классы/Libraries.cs
+++ b/Млаумчерб.Клиент/классы/Libraries.cs
@@ -1,4 +1,5 @@
-using DTLib.Extensions;
+using System.Runtime.InteropServices;
+using DTLib.Extensions;
namespace Млаумчерб.Клиент.классы;
@@ -35,6 +36,23 @@ public class Libraries
if(nativesKey is null)
throw new Exception($"nativesKey for '{l.name}' is null");
+ // example: "natives-windows-${arch}"
+ if (nativesKey.Contains('$'))
+ {
+ var span = nativesKey.AsSpan();
+ nativesKey = span.After("${").Before('}') switch
+ {
+ "arch" => RuntimeInformation.OSArchitecture switch
+ {
+ Architecture.X64 => span.Before("${").ToString() + "64",
+ Architecture.X86 => span.Before("${").ToString() + "32",
+ _ => throw new PlatformNotSupportedException(
+ $"Unsupported architecture: {RuntimeInformation.OSArchitecture}")
+ },
+ _ => throw new Exception($"unknown placeholder in {nativesKey}")
+ };
+ }
+
Artifact artifact = null!;
if(l.downloads.classifiers != null && !l.downloads.classifiers.TryGetValue(nativesKey, out artifact!))
throw new Exception($"can't find artifact for '{l.name}' with nativesKey '{nativesKey}'");
diff --git a/Млаумчерб.Клиент/классы/Буржуазия.cs b/Млаумчерб.Клиент/классы/Буржуазия.cs
index cf10e79..6bd2388 100644
--- a/Млаумчерб.Клиент/классы/Буржуазия.cs
+++ b/Млаумчерб.Клиент/классы/Буржуазия.cs
@@ -17,7 +17,7 @@ public static class Буржуазия
&& os.arch switch
{
null => true,
- "x86" => RuntimeInformation.OSArchitecture == Architecture.X86,
+ "i386" or "x86" => RuntimeInformation.OSArchitecture == Architecture.X86,
"x64" => RuntimeInformation.OSArchitecture == Architecture.X64,
"arm64" => RuntimeInformation.OSArchitecture == Architecture.Arm64,
_ => false
diff --git a/Млаумчерб.Клиент/классы/Пути.cs b/Млаумчерб.Клиент/классы/Пути.cs
index 4052b69..1c19999 100644
--- a/Млаумчерб.Клиент/классы/Пути.cs
+++ b/Млаумчерб.Клиент/классы/Пути.cs
@@ -1,4 +1,4 @@
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
namespace Млаумчерб.Клиент.классы;
@@ -7,14 +7,14 @@ public static class Пути
public static IOPath GetAssetIndexFilePath(string id) =>
Path.Concat(Приложение.Настройки.путь_к_кубачу, $"assets/indexes/{id}.json");
- public static IOPath GetVersionDescriptorDir() =>
- Path.Concat(Приложение.Настройки.путь_к_кубачу, "version_descriptors");
+ public static IOPath GetVersionDescriptorsDir() =>
+ Path.Concat(Приложение.Настройки.путь_к_кубачу, "versions");
public static string GetVersionDescriptorName(IOPath path) =>
path.LastName().RemoveExtension().ToString();
public static IOPath GetVersionDescriptorPath(string name) =>
- Path.Concat(GetVersionDescriptorDir(), Path.ReplaceRestrictedChars(name) + ".json");
+ Path.Concat(GetVersionDescriptorsDir(), Path.ReplaceRestrictedChars(name) + ".json");
public static IOPath GetVersionDir(string id) =>
Path.Concat(Приложение.Настройки.путь_к_кубачу, "versions", id);
diff --git a/Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs b/Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs
index cfe29a8..e8a1f39 100644
--- a/Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs
+++ b/Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs
@@ -1,4 +1,4 @@
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
using Timer = DTLib.Timer;
namespace Млаумчерб.Клиент.сеть;
@@ -28,10 +28,9 @@ public class NetworkProgressReporter : IDisposable
}
// atomic add
- public void AddBytesCount(ArraySegment chunk)
+ public void AddBytesCount(byte[] buffer, int offset, int count)
{
- long chunkSize = chunk.Count;
- Interlocked.Add(ref _curSize, chunkSize);
+ Interlocked.Add(ref _curSize, count);
}
public void Start()
diff --git a/Млаумчерб.Клиент/сеть/NetworkTask.cs b/Млаумчерб.Клиент/сеть/NetworkTask.cs
index c2e724c..d3dca72 100644
--- a/Млаумчерб.Клиент/сеть/NetworkTask.cs
+++ b/Млаумчерб.Клиент/сеть/NetworkTask.cs
@@ -7,7 +7,7 @@ public class NetworkTask : IDisposable
public enum Status
{
Initialized,
- Running,
+ Started,
Completed,
Cancelled,
Failed
@@ -15,6 +15,7 @@ public class NetworkTask : IDisposable
public Status DownloadStatus { get; private set; } = Status.Initialized;
+ public event Action? OnStart;
public event Action? OnProgress;
public event Action? OnStop;
@@ -35,15 +36,20 @@ public class NetworkTask : IDisposable
public async Task StartAsync()
{
- if(DownloadStatus == Status.Running || DownloadStatus == Status.Completed)
+ if(DownloadStatus == Status.Started || DownloadStatus == Status.Completed)
return;
- DownloadStatus = Status.Running;
+ DownloadStatus = Status.Started;
try
{
_progressReporter.Start();
+ OnStart?.Invoke();
await _downloadAction(_progressReporter, _cts.Token);
DownloadStatus = Status.Completed;
}
+ catch (OperationCanceledException)
+ {
+ DownloadStatus = Status.Cancelled;
+ }
catch
{
DownloadStatus = Status.Failed;
diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs
index 3e5b202..90fbe3f 100644
--- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs
+++ b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs
@@ -1,6 +1,8 @@
-using System.Security.Cryptography;
+using System.Net;
+using System.Net.Http;
+using System.Security.Cryptography;
using DTLib.Extensions;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
using Млаумчерб.Клиент.классы;
using static Млаумчерб.Клиент.сеть.Сеть;
@@ -37,7 +39,7 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
if(!File.Exists(_indexFilePath))
{
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading asset index to '{_indexFilePath}'");
- await DownloadFileHTTP(_descriptor.assetIndex.url, _indexFilePath);
+ await DownloadFile(_descriptor.assetIndex.url, _indexFilePath);
Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading asset index");
}
@@ -86,7 +88,7 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
public string url;
public IOPath filePath;
- public AssetDownloadProperties(string key, GameAssetProperties p)
+ public AssetDownloadProperties(string key, AssetProperties p)
{
name = key;
hash = p.hash;
@@ -100,13 +102,35 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
{
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading assets '{_descriptor.assetIndex.id}'");
- ParallelOptions opt = new() { MaxDegreeOfParallelism = ParallelDownloadsN, CancellationToken = ct };
+ ParallelOptions opt = new()
+ {
+ MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок,
+ CancellationToken = ct
+ };
await Parallel.ForEachAsync(_assetsToDownload, opt,
- async (a, _ct) =>
+ async (a, _ct) =>
+ {
+ bool completed = false;
+ while(!completed)
{
Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading asset '{a.name}' {a.hash}");
- await DownloadFileHTTP(a.url, a.filePath, _ct, pr.AddBytesCount);
- });
+ try
+ {
+ await DownloadFile(a.url, a.filePath, _ct, pr.AddBytesCount);
+ completed = true;
+ }
+ catch (HttpRequestException httpException)
+ {
+ // wait on rate limit
+ if(httpException.StatusCode == HttpStatusCode.TooManyRequests)
+ {
+ Приложение.Логгер.LogDebug(nameof(Сеть), "rate limit hit");
+ await Task.Delay(1000, _ct);
+ }
+ else throw;
+ }
+ }
+ });
Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading assets '{_descriptor.assetIndex.id}'");
}
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs
index df02e14..96532e0 100644
--- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs
+++ b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs
@@ -1,5 +1,6 @@
using System.Security.Cryptography;
-using EasyCompressor;
+using DTLib.Extensions;
+using Млаумчерб.Клиент.зримое;
using Млаумчерб.Клиент.классы;
using static Млаумчерб.Клиент.сеть.Сеть;
@@ -7,23 +8,27 @@ namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories;
public class JavaDownloadTaskFactory : INetworkTaskFactory
{
- private const string INDEX_URL =
+ private const string CATALOG_URL =
"https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json";
private GameVersionDescriptor _descriptor;
private IOPath _javaVersionDir;
private SHA1 _hasher;
- private LZMACompressor _lzma;
+ private JavaDistributiveManifest? _distributiveManifest;
+ private List<(IOPath path, JavaDistributiveElementProps props)> _filesToDownload = new();
public JavaDownloadTaskFactory(GameVersionDescriptor descriptor)
{
_descriptor = descriptor;
_javaVersionDir = Пути.GetJavaRuntimeDir(_descriptor.javaVersion.component);
_hasher = SHA1.Create();
- _lzma = new LZMACompressor();
}
- public Task CreateAsync(bool checkHashes)
+ public async Task CreateAsync(bool checkHashes)
{
+ var catalog = await DownloadStringAndDeserialize(CATALOG_URL);
+ var versionProps = catalog.GetVersionProps(_descriptor.javaVersion);
+ _distributiveManifest = await DownloadStringAndDeserialize(versionProps.manifest.url);
+
NetworkTask? networkTask = null;
if (!CheckFiles(checkHashes))
networkTask = new(
@@ -31,26 +36,87 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory
GetTotalSize(),
Download
);
- return Task.FromResult(networkTask);
+ return networkTask;
}
private bool CheckFiles(bool checkHashes)
{
- //TODO: download catalog
- //TODO: download manifest for required runtime
- //TODO: check whether files from manifest exist and match hashes
- throw new NotImplementedException();
+ _filesToDownload.Clear();
+ foreach (var pair in _distributiveManifest!.files)
+ {
+ if (pair.Value.type != "file")
+ continue;
+
+ if (pair.Value.downloads != null)
+ {
+ var artifact = pair.Value.downloads;
+ IOPath file_path = Path.Concat(_javaVersionDir, pair.Key);
+ if (!File.Exists(file_path))
+ {
+ _filesToDownload.Add((file_path, pair.Value));
+ }
+ else if(checkHashes)
+ {
+ using var fs = File.OpenRead(file_path);
+ if (_hasher.ComputeHash(fs).HashToString() != artifact.raw.sha1)
+ {
+ _filesToDownload.Add((file_path, pair.Value));
+ }
+ }
+ }
+ }
+
+ return _filesToDownload.Count == 0;
}
private long GetTotalSize()
{
- //TODO: sum up size of all files invalidated by CheckFiles
- throw new NotImplementedException();
+ long totalSize = 0;
+ foreach (var file in _filesToDownload)
+ {
+ if(file.props.downloads == null)
+ continue;
+ totalSize += file.props.downloads.lzma?.size ?? file.props.downloads.raw.size;
+ }
+ return totalSize;
}
- private Task Download(NetworkProgressReporter pr, CancellationToken ct)
+ private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
{
- //TODO: download files using lzma decompression
- throw new NotImplementedException();
+ Приложение.Логгер.LogInfo(nameof(Сеть), "started downloading java runtime " +
+ $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
+
+ ParallelOptions opt = new()
+ {
+ MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок,
+ CancellationToken = ct
+ };
+ await Parallel.ForEachAsync(_filesToDownload, opt, async (f, _ct) =>
+ {
+ if (f.props.downloads!.lzma != null)
+ {
+ Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading lzma-compressed file '{f.path}'");
+ await using var pipe = new TransformStream(await GetStream(f.props.downloads.lzma.url, _ct));
+ pipe.AddTransform(pr.AddBytesCount);
+ await using var fs = File.OpenWrite(f.path);
+ LZMACompressor.Decompress(pipe, fs);
+ }
+ else
+ {
+ Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading raw file '{f.path}'");
+ await DownloadFile(f.props.downloads.raw.url, f.path, _ct, pr.AddBytesCount);
+ }
+
+ if(!OperatingSystem.IsWindows() && f.props.executable is true)
+ {
+ Приложение.Логгер.LogDebug(nameof(Сеть), $"adding execute rights to file '{f.path}'");
+ System.IO.File.SetUnixFileMode(f.path.ToString(), UnixFileMode.UserExecute);
+ }
+ });
+
+ Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading java runtime " +
+ $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
}
+
+
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs
index b6bb52c..06f6689 100644
--- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs
+++ b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs
@@ -1,7 +1,7 @@
using System.IO.Compression;
using System.Security.Cryptography;
using DTLib.Extensions;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
using Млаумчерб.Клиент.классы;
using static Млаумчерб.Клиент.сеть.Сеть;
@@ -73,12 +73,16 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
{
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading libraries '{_descriptor.id}'");
- ParallelOptions opt = new() { MaxDegreeOfParallelism = ParallelDownloadsN, CancellationToken = ct };
+
+ ParallelOptions opt = new()
+ {
+ MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок,
+ CancellationToken = ct
+ };
await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) =>
{
- Приложение.Логгер.LogDebug(nameof(Сеть),
- $"downloading library '{l.name}' to '{l.jarFilePath}'");
- await DownloadFileHTTP(l.artifact.url, l.jarFilePath, _ct, pr.AddBytesCount);
+ Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading library '{l.name}' to '{l.jarFilePath}'");
+ await DownloadFile(l.artifact.url, l.jarFilePath, _ct, pr.AddBytesCount);
if (l is Libraries.NativeLib n)
{
var zipf = File.OpenRead(n.jarFilePath);
@@ -96,6 +100,7 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
}
}
});
+
Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading libraries '{_descriptor.id}'");
}
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs
index 2488ed4..d851420 100644
--- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs
+++ b/Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs
@@ -1,6 +1,6 @@
using System.Security.Cryptography;
using DTLib.Extensions;
-using Млаумчерб.Клиент.видимое;
+using Млаумчерб.Клиент.зримое;
using Млаумчерб.Клиент.классы;
using static Млаумчерб.Клиент.сеть.Сеть;
@@ -50,7 +50,7 @@ public class VersionFileDownloadTaskFactory : INetworkTaskFactory
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
{
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading version file '{_descriptor.id}'");
- await DownloadFileHTTP(_descriptor.downloads.client.url, _filePath, ct, pr.AddBytesCount);
+ await DownloadFile(_descriptor.downloads.client.url, _filePath, ct, pr.AddBytesCount);
Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading version file '{_descriptor.id}'");
}
}
\ No newline at end of file
diff --git a/Млаумчерб.Клиент/сеть/Сеть.cs b/Млаумчерб.Клиент/сеть/Сеть.cs
index a6b8dc2..6e2f97a 100644
--- a/Млаумчерб.Клиент/сеть/Сеть.cs
+++ b/Млаумчерб.Клиент/сеть/Сеть.cs
@@ -1,53 +1,46 @@
-using System.Buffers;
-using System.Net.Http;
-using Млаумчерб.Клиент.видимое;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using Млаумчерб.Клиент.зримое;
using Млаумчерб.Клиент.классы;
namespace Млаумчерб.Клиент.сеть;
public static class Сеть
{
- public static int ParallelDownloadsN = 32;
- public static HttpClient http = new();
+ private static HttpClient _http = new();
- public static async Task DownloadFileHTTP(string url, IOPath outPath, CancellationToken ct = default,
- Action>? transformFunc = null)
+ static Сеть()
{
- await using var src = await http.GetStreamAsync(url, ct);
- await using var dst = File.OpenWrite(outPath);
-
- await src.CopyTransformAsync(dst, transformFunc, ct).ConfigureAwait(false);
+ // thanks for Sashok :3
+ // https://github.com/new-sashok724/Launcher/blob/23485c3f7de6620d2c6b7b2dd9339c3beb6a0366/Launcher/source/helper/IOHelper.java#L259
+ _http.DefaultRequestHeaders.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
}
- public static async Task CopyTransformAsync(this Stream src, Stream dst,
- Action>? transformFunc = null, CancellationToken ct = default)
+ public static Task GetString(string url, CancellationToken ct = default) => _http.GetStringAsync(url, ct);
+ public static Task GetStream(string url, CancellationToken ct = default) => _http.GetStreamAsync(url, ct);
+
+ public static async Task DownloadFile(string url, Stream outStream, CancellationToken ct = default,
+ params TransformStream.TransformFuncDelegate[] transforms)
{
- // default dotnet runtime buffer size
- int bufferSize = 81920;
- byte[] readBuffer = ArrayPool.Shared.Rent(bufferSize);
- byte[] writeBuffer = ArrayPool.Shared.Rent(bufferSize);
- try
- {
- var readTask = src.ReadAsync(readBuffer, 0, bufferSize, ct).ConfigureAwait(false);
- while (true)
- {
- int readCount = await readTask;
- if (readCount == 0)
- break;
- (readBuffer, writeBuffer) = (writeBuffer, readBuffer);
- readTask = src.ReadAsync(readBuffer, 0, bufferSize, ct).ConfigureAwait(false);
- transformFunc?.Invoke(new ArraySegment(writeBuffer, 0, readCount));
- dst.Write(writeBuffer, 0, readCount);
- }
- }
- catch (OperationCanceledException)
- {
- }
- finally
- {
- ArrayPool.Shared.Return(readBuffer);
- ArrayPool.Shared.Return(writeBuffer);
- }
+ await using var pipe = new TransformStream(await GetStream(url, ct));
+ if (transforms.Length > 0)
+ pipe.AddTransforms(transforms);
+ await pipe.CopyToAsync(outStream, ct);
+ }
+
+ public static async Task DownloadFile(string url, IOPath outPath, CancellationToken ct = default,
+ params TransformStream.TransformFuncDelegate[] transforms)
+ {
+ await using var file = File.OpenWrite(outPath);
+ await DownloadFile(url, file, ct, transforms);
+ }
+
+ public static async Task DownloadStringAndDeserialize(string url)
+ {
+ var text = await _http.GetStringAsync(url);
+ var result = JsonConvert.DeserializeObject(text)
+ ?? throw new Exception($"can't deserialize {typeof(T).Name}");
+ return result;
}
private static readonly string[] VERSION_MANIFEST_URLS =
@@ -62,10 +55,8 @@ public static class Сеть
{
try
{
- var manifestText = await http.GetStringAsync(url);
- var catalog = JsonConvert.DeserializeObject(manifestText);
- if (catalog != null)
- descriptors.AddRange(catalog.versions);
+ var catalog = await DownloadStringAndDeserialize(url);
+ descriptors.AddRange(catalog.versions);
}
catch (Exception ex)
{
diff --git a/млаумчерб.sln b/млаумчерб.sln
index eeea37e..4a36c11 100644
--- a/млаумчерб.sln
+++ b/млаумчерб.sln
@@ -4,7 +4,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Млаумчерб.Клие
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{A3217C18-CC0D-4CE8-9C48-1BDEC1E1B333}"
ProjectSection(SolutionItems) = preProject
- nuget.config = nuget.config
+ .gitignore = .gitignore
EndProjectSection
EndProject
Global