diff --git a/Mlaumcherb.Client.Avalonia/Config.cs b/Mlaumcherb.Client.Avalonia/Config.cs new file mode 100644 index 0000000..058dadd --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/Config.cs @@ -0,0 +1,66 @@ +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.холопы; + +namespace Mlaumcherb.Client.Avalonia; + +public record Config +{ + public bool debug { get; set; } = +#if DEBUG + true; +#else + false; +#endif + public string player_name { get; set; } = ""; + public int max_memory { get; set; } = 4096; + public string minecraft_dir { get; set; } = "."; + public bool download_java { get; set; } = true; + public string? last_launched_version { get; set; } + public int max_parallel_downloads { get; set; } = 16; + + [JsonIgnore] static IOPath _filePath = "config.json"; + + public static Config LoadFromFile() + { + LauncherApp.Logger.LogInfo(nameof(Config), $"loading config from file '{_filePath}'"); + if(!File.Exists(_filePath)) + { + LauncherApp.Logger.LogInfo(nameof(Config), "file doesn't exist"); + return new Config(); + } + + string text = File.ReadAllText(_filePath); + string errorMessage = "config is empty"; + Config? config = null; + try + { + config = JsonConvert.DeserializeObject(text); + } + catch (Exception ex) + { + errorMessage = ex.Message; + } + if (config == null) + { + IOPath _brokenPath = _filePath + ".broken"; + File.Move(_filePath, _brokenPath, true); + ErrorHelper.ShowMessageBox(nameof(Config), + $"Can't reed config file '{_filePath}'.\n" + + $"New config file has been created. Old is renamed to '{_brokenPath}'.\n\n" + + $"Config parser error: {errorMessage}"); + config = new Config(); + config.SaveToFile(); + } + + LauncherApp.Logger.LogDebug(nameof(Config), $"config has been loaded: {config}"); + return config; + } + + public void SaveToFile() + { + LauncherApp.Logger.LogInfo(nameof(Config), $"saving config to file '{_filePath}'"); + var text = JsonConvert.SerializeObject(this, Formatting.Indented); + File.WriteAllText(_filePath, text); + LauncherApp.Logger.LogDebug(nameof(Config), $"config has been saved: {text}"); + } +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/GameVersion.cs b/Mlaumcherb.Client.Avalonia/GameVersion.cs new file mode 100644 index 0000000..b257354 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/GameVersion.cs @@ -0,0 +1,229 @@ +using System.Linq; +using System.Security.Cryptography; +using CliWrap; +using DTLib.Extensions; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.сеть; +using Mlaumcherb.Client.Avalonia.сеть.TaskFactories; +using Mlaumcherb.Client.Avalonia.холопы; +using static Mlaumcherb.Client.Avalonia.холопы.PathHelper; + +namespace Mlaumcherb.Client.Avalonia; + +public class GameVersion +{ + private readonly GameVersionProps _props; + public string Name => _props.Name; + public IOPath WorkingDirectory { get; } + + private IOPath JavaExecutableFilePath; + + private GameVersionDescriptor _descriptor; + private JavaArguments _javaArgs; + private GameArguments _gameArgs; + private Libraries _libraries; + private CancellationTokenSource? _gameCts; + private CommandTask? _commandTask; + + public static async Task> GetAllVersionsAsync() + { + var propsSet = new HashSet(); + + // local descriptors + Directory.Create(GetVersionsDir()); + foreach (IOPath subdir in Directory.GetDirectories(GetVersionsDir())) + { + string name = subdir.LastName().ToString(); + var d = new GameVersionProps(name, null); + if(!File.Exists(d.LocalDescriptorPath)) + throw new Exception("Can't find version descriptor file in directory '{subdir}'. Rename it as directory name."); + propsSet.Add(d); + } + + // 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; + } + + public static async Task CreateFromPropsAsync(GameVersionProps props) + { + if (!File.Exists(props.LocalDescriptorPath)) + { + if (props.RemoteDescriptorUrl is null) + throw new NullReferenceException("can't download game version descriptor '" + + props.Name + "', because RemoteDescriptorUrl is null"); + await Сеть.DownloadFile(props.RemoteDescriptorUrl, props.LocalDescriptorPath); + } + + return new GameVersion(props); + } + + private GameVersion(GameVersionProps props) + { + _props = props; + string descriptorText = File.ReadAllText(props.LocalDescriptorPath); + _descriptor = JsonConvert.DeserializeObject(descriptorText) + ?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'"); + _javaArgs = new JavaArguments(_descriptor); + _gameArgs = new GameArguments(_descriptor); + _libraries = new Libraries(_descriptor); + WorkingDirectory = GetVersionDir(_descriptor.id); + JavaExecutableFilePath = GetJavaExecutablePath(_descriptor.javaVersion.component, LauncherApp.Config.debug); + } + + public async Task UpdateFiles(bool checkHashes, Action networkTaskCreatedCallback) + { + LauncherApp.Logger.LogInfo(Name, $"started updating version {Name}"); + + List taskFactories = + [ + new AssetsDownloadTaskFactory(_descriptor), + new LibrariesDownloadTaskFactory(_descriptor, _libraries), + new VersionFileDownloadTaskFactory(_descriptor), + ]; + if(LauncherApp.Config.download_java) + { + taskFactories.Add(new JavaDownloadTaskFactory(_descriptor)); + } + //TODO: modpack + /*if (modpack != null) + { + taskFactories.Add(new ModpackDownloadTaskFactory(modpack)); + }*/ + + var networkTasks = new List(); + for (int i = 0; i < taskFactories.Count; i++) + { + var nt = await taskFactories[i].CreateAsync(checkHashes); + if (nt != null) + { + networkTasks.Add(nt); + networkTaskCreatedCallback.Invoke(nt); + } + } + + foreach (var nt in networkTasks) + { + await nt.StartAsync(); + } + + // create log4j config file + if (!File.Exists(GetLog4jConfigFilePath())) + { + await using var res = EmbeddedResources.GetResourceStream( + $"Mlaumcherb.Client.Avalonia.встроенное.{GetLog4jConfigFileName()}"); + await using var file = File.OpenWrite(GetLog4jConfigFilePath()); + await res.CopyToAsync(file); + } + + _props.IsDownloaded = true; + LauncherApp.Logger.LogInfo(Name, $"finished updating version {Name}"); + } + + //minecraft player uuid explanation + //https://gist.github.com/CatDany/0e71ca7cd9b42a254e49/ + //java uuid generation in c# + //https://stackoverflow.com/questions/18021808/uuid-interop-with-c-sharp-code + public static string GetPlayerUUID(string name) + { + byte[] name_bytes = Encoding.UTF8.GetBytes("OfflinePlayer:" + name); + var md5 = MD5.Create(); + byte[] hash = md5.ComputeHash(name_bytes); + hash[6] &= 0x0f; + hash[6] |= 0x30; + hash[8] &= 0x3f; + hash[8] |= 0x80; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hash.Length; i++) + sb.Append(hash[i].ToString("x2")); + sb.Insert(8, '-').Insert(13, '-').Insert(18, '-').Insert(23, '-'); + return sb.ToString(); + } + + public async Task Launch() + { + if (string.IsNullOrWhiteSpace(LauncherApp.Config.player_name)) + throw new Exception("invalid player name"); + + Directory.Create(WorkingDirectory); + string uuid = GetPlayerUUID(LauncherApp.Config.player_name); + Dictionary placeholder_values = new() { + { "resolution_width", "1600" }, + { "resolution_height", "900" }, + { "xms", "2048" }, + { "xmx", LauncherApp.Config.max_memory.ToString() }, + { "auth_player_name", LauncherApp.Config.player_name }, + { "auth_access_token", uuid }, + { "auth_session", $"token:{uuid}:{uuid}" }, + { "auth_xuid", "" }, + { "auth_uuid", uuid }, + { "clientid", "" }, + { "user_properties", "{}" }, + { "user_type", "userType" }, + { "launcher_name", "java-minecraft-launcher" }, + { "launcher_version", "1.6.84-j" }, + { "classpath", _libraries.Libs.Select(l => l.jarFilePath) + .Append(GetVersionJarFilePath(_descriptor.id)) + .MergeToString(';') }, + { "assets_index_name", _descriptor.assets }, + { "assets_root", GetAssetsDir().ToString() }, + { "game_assets", GetAssetsDir().ToString() }, + { "game_directory", WorkingDirectory.ToString() }, + { "natives_directory", GetNativeLibrariesDir(_descriptor.id).ToString() }, + { "path", GetLog4jConfigFilePath().ToString() }, + { "version_name", _descriptor.id }, + { "version_type", _descriptor.type }, + { "arch", PlatformHelper.GetArchOld() }, + // { "quickPlayMultiplayer", "" }, + // { "quickPlayPath", "" }, + // { "quickPlayRealms", "" }, + // { "quickPlaySingleplayer", "" }, + { "client_jar", GetVersionJarFilePath(_descriptor.id).ToString() }, + }; + + List argsList = new(); + argsList.AddRange(_javaArgs.FillPlaceholders(placeholder_values)); + argsList.Add(_descriptor.mainClass); + argsList.AddRange(_gameArgs.FillPlaceholders(placeholder_values)); + var command = Cli.Wrap(JavaExecutableFilePath.ToString()) + .WithWorkingDirectory(WorkingDirectory.ToString()) + .WithArguments(argsList) + .WithStandardOutputPipe(PipeTarget.ToDelegate(LogGameOut)) + .WithStandardErrorPipe(PipeTarget.ToDelegate(LogGameError)) + .WithValidation(CommandResultValidation.None); + LauncherApp.Logger.LogInfo(Name, "launching the game"); + LauncherApp.Logger.LogDebug(Name, "java: " + command.TargetFilePath); + LauncherApp.Logger.LogDebug(Name, "working_dir: " + command.WorkingDirPath); + LauncherApp.Logger.LogDebug(Name, "arguments: \n\t" + argsList.MergeToString("\n\t")); + _gameCts = new(); + _commandTask = command.ExecuteAsync(_gameCts.Token); + var result = await _commandTask; + LauncherApp.Logger.LogInfo(Name, $"game exited with code {result.ExitCode}"); + } + + private void LogGameOut(string line) + { + LauncherApp.Logger.LogInfo(Name, line); + } + private void LogGameError(string line) + { + LauncherApp.Logger.LogWarn(Name, line); + } + + public void Close() + { + _gameCts?.Cancel(); + } + + + public override string ToString() => Name; +} \ No newline at end of file diff --git a/Млаумчерб.Клиент/Логи.cs b/Mlaumcherb.Client.Avalonia/LauncherLogger.cs similarity index 92% rename from Млаумчерб.Клиент/Логи.cs rename to Mlaumcherb.Client.Avalonia/LauncherLogger.cs index 87ca487..e1028f1 100644 --- a/Млаумчерб.Клиент/Логи.cs +++ b/Mlaumcherb.Client.Avalonia/LauncherLogger.cs @@ -1,4 +1,6 @@ -namespace Млаумчерб.Клиент; +using Mlaumcherb.Client.Avalonia.зримое; + +namespace Mlaumcherb.Client.Avalonia; public class LauncherLogger : ILogger { @@ -18,10 +20,6 @@ public class LauncherLogger : ILogger #endif ]; _compositeLogger = new CompositeLogger(loggers); - -#if DEBUG - DebugLogEnabled = true; -#endif } public record LogMessage(string context, LogSeverity severity, object message, ILogFormat format); diff --git a/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj b/Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj similarity index 74% rename from Млаумчерб.Клиент/Млаумчерб.Клиент.csproj rename to Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj index 4383775..8db2004 100644 --- a/Млаумчерб.Клиент/Млаумчерб.Клиент.csproj +++ b/Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj @@ -30,21 +30,7 @@ + - - - - VersionItemView.axaml - Code - - - Окне.axaml - Code - - - Приложение.axaml - Code - - - + diff --git a/Млаумчерб.Клиент/Главне.cs b/Mlaumcherb.Client.Avalonia/Program.cs similarity index 77% rename from Млаумчерб.Клиент/Главне.cs rename to Mlaumcherb.Client.Avalonia/Program.cs index b395293..c039196 100644 --- a/Млаумчерб.Клиент/Главне.cs +++ b/Mlaumcherb.Client.Avalonia/Program.cs @@ -14,11 +14,11 @@ global using Directory = DTLib.Filesystem.Directory; global using Path = DTLib.Filesystem.Path; using System.Globalization; using Avalonia; -using Млаумчерб.Клиент.зримое; +using Mlaumcherb.Client.Avalonia.зримое; -namespace Млаумчерб.Клиент; +namespace Mlaumcherb.Client.Avalonia; -public class Главне +public class Program { [STAThread] public static void Main(string[] args) @@ -31,13 +31,13 @@ public class Главне } catch (Exception ex) { - Приложение.Логгер.LogError(nameof(Главне), ex); + LauncherApp.Logger.LogError(nameof(Program), ex); } } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure<Приложение>() + => AppBuilder.Configure() .UsePlatformDetect() .LogToTrace(); } \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/встроенное/log4j.xml b/Mlaumcherb.Client.Avalonia/встроенное/log4j.xml new file mode 100644 index 0000000..824e26c --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/встроенное/log4j.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Млаумчерб.Клиент/гойда.manifest b/Mlaumcherb.Client.Avalonia/гойда.manifest similarity index 100% rename from Млаумчерб.Клиент/гойда.manifest rename to Mlaumcherb.Client.Avalonia/гойда.manifest diff --git a/Млаумчерб.Клиент/зримое/DownloadItemView.axaml b/Mlaumcherb.Client.Avalonia/зримое/DownloadItemView.axaml similarity index 90% rename from Млаумчерб.Клиент/зримое/DownloadItemView.axaml rename to Mlaumcherb.Client.Avalonia/зримое/DownloadItemView.axaml index 71ee5e5..5d84242 100644 --- a/Млаумчерб.Клиент/зримое/DownloadItemView.axaml +++ b/Mlaumcherb.Client.Avalonia/зримое/DownloadItemView.axaml @@ -1,6 +1,6 @@  diff --git a/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs b/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs new file mode 100644 index 0000000..e1c8e48 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs @@ -0,0 +1,29 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace Mlaumcherb.Client.Avalonia.зримое; + +public class LauncherApp : Application +{ + public static LauncherLogger Logger = new(); + public static Config Config = new(); + + public override void Initialize() + { + Logger.LogInfo(nameof(LauncherApp), "приложение запущено"); + Config = Config.LoadFromFile(); + Logger.DebugLogEnabled = Config.debug; + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/Млаумчерб.Клиент/зримое/LogMessageView.axaml b/Mlaumcherb.Client.Avalonia/зримое/LogMessageView.axaml similarity index 87% rename from Млаумчерб.Клиент/зримое/LogMessageView.axaml rename to Mlaumcherb.Client.Avalonia/зримое/LogMessageView.axaml index a5c3762..6245185 100644 --- a/Млаумчерб.Клиент/зримое/LogMessageView.axaml +++ b/Mlaumcherb.Client.Avalonia/зримое/LogMessageView.axaml @@ -3,7 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Млаумчерб.Клиент.зримое.LogMessageView" + x:Class="Mlaumcherb.Client.Avalonia.зримое.LogMessageView" FontSize="14"> @@ -30,7 +30,7 @@ Ник: + Text="{Binding #window.PlayerName}"/> Выделенная память: @@ -45,9 +45,6 @@ Value="{Binding #window.MemoryLimit}"> - - Запускать полноэкранное - Проверять файлы игры diff --git a/Млаумчерб.Клиент/зримое/Окне.axaml.cs b/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs similarity index 60% rename from Млаумчерб.Клиент/зримое/Окне.axaml.cs rename to Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs index f139f10..238eabe 100644 --- a/Млаумчерб.Клиент/зримое/Окне.axaml.cs +++ b/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml.cs @@ -4,41 +4,33 @@ using Avalonia.Data; using Avalonia.Interactivity; using Avalonia.Platform.Storage; using Avalonia.Threading; -using Млаумчерб.Клиент.классы; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; -namespace Млаумчерб.Клиент.зримое; +namespace Mlaumcherb.Client.Avalonia.зримое; -public partial class Окне : Window +public partial class MainWindow : Window { - public static readonly StyledProperty UsernameProperty = - AvaloniaProperty.Register<Окне, string>(nameof(Username), + public static readonly StyledProperty PlayerNameProperty = + AvaloniaProperty.Register(nameof(PlayerName), defaultBindingMode: BindingMode.TwoWay); - public string Username + public string PlayerName { - get => GetValue(UsernameProperty); - set => SetValue(UsernameProperty, value); + get => GetValue(PlayerNameProperty); + set => SetValue(PlayerNameProperty, value); } public static readonly StyledProperty MemoryLimitProperty = - AvaloniaProperty.Register<Окне, int>(nameof(MemoryLimit), + AvaloniaProperty.Register(nameof(MemoryLimit), defaultBindingMode: BindingMode.TwoWay, defaultValue: 2048); public int MemoryLimit { get => GetValue(MemoryLimitProperty); set => SetValue(MemoryLimitProperty, value); } - - public static readonly StyledProperty FullscreenProperty = - AvaloniaProperty.Register<Окне, bool>(nameof(Fullscreen), - defaultBindingMode: BindingMode.TwoWay, defaultValue: false); - public bool Fullscreen - { - get => GetValue(FullscreenProperty); - set => SetValue(FullscreenProperty, value); - } public static readonly StyledProperty CheckGameFilesProperty = - AvaloniaProperty.Register<Окне, bool>(nameof(CheckGameFiles), + AvaloniaProperty.Register(nameof(CheckGameFiles), defaultBindingMode: BindingMode.TwoWay, defaultValue: false); public bool CheckGameFiles { @@ -48,7 +40,7 @@ public partial class Окне : Window public static readonly StyledProperty EnableJavaDownloadProperty = - AvaloniaProperty.Register<Окне, bool>(nameof(EnableJavaDownload), + AvaloniaProperty.Register(nameof(EnableJavaDownload), defaultBindingMode: BindingMode.TwoWay, defaultValue: true); public bool EnableJavaDownload { @@ -56,7 +48,7 @@ public partial class Окне : Window set => SetValue(EnableJavaDownloadProperty, value); } - public Окне() + public MainWindow() { InitializeComponent(); } @@ -65,14 +57,12 @@ public partial class Окне : Window { try { - Приложение.Логгер.OnLogMessage += GuiLogMessage; + LauncherApp.Logger.OnLogMessage += GuiLogMessage; - Username = Приложение.Настройки.имя_пользователя; - MemoryLimit = Приложение.Настройки.выделенная_память_мб; - Fullscreen = Приложение.Настройки.запускать_полноэкранное; - EnableJavaDownload = Приложение.Настройки.скачивать_жабу; + PlayerName = LauncherApp.Config.player_name; + MemoryLimit = LauncherApp.Config.max_memory; + EnableJavaDownload = LauncherApp.Config.download_java; - Directory.Create(Пути.GetVersionDescriptorsDir()); VersionComboBox.SelectedIndex = 0; VersionComboBox.IsEnabled = false; var versions = await GameVersion.GetAllVersionsAsync(); @@ -81,8 +71,8 @@ public partial class Окне : Window foreach (var p in versions) { VersionComboBox.Items.Add(new VersionItemView(p)); - if (Приложение.Настройки.последняя_запущенная_версия != null && - p.Name == Приложение.Настройки.последняя_запущенная_версия) + if (LauncherApp.Config.last_launched_version != null && + p.Name == LauncherApp.Config.last_launched_version) VersionComboBox.SelectedIndex = VersionComboBox.Items.Count - 1; } VersionComboBox.IsEnabled = true; @@ -90,7 +80,7 @@ public partial class Окне : Window } catch (Exception ex) { - Ошибки.ПоказатьСообщение(nameof(Окне), ex); + ErrorHelper.ShowMessageBox(nameof(MainWindow), ex); } } @@ -115,12 +105,11 @@ public partial class Окне : Window var selectedVersionView = (VersionItemView?)VersionComboBox.SelectedItem; var selectedVersion = selectedVersionView?.Props; - Приложение.Настройки.последняя_запущенная_версия = selectedVersion?.Name; - Приложение.Настройки.имя_пользователя = Username; - Приложение.Настройки.выделенная_память_мб = MemoryLimit; - Приложение.Настройки.запускать_полноэкранное = Fullscreen; - Приложение.Настройки.скачивать_жабу = EnableJavaDownload; - Приложение.Настройки.СохранитьВФайл(); + LauncherApp.Config.last_launched_version = selectedVersion?.Name; + LauncherApp.Config.player_name = PlayerName; + LauncherApp.Config.max_memory = MemoryLimit; + LauncherApp.Config.download_java = EnableJavaDownload; + LauncherApp.Config.SaveToFile(); if (selectedVersion == null) return; @@ -138,10 +127,11 @@ public partial class Окне : Window { CheckGameFiles = false; }); + await v.Launch(); } catch (Exception ex) { - Ошибки.ПоказатьСообщение(nameof(Окне), ex); + ErrorHelper.ShowMessageBox(nameof(MainWindow), ex); } finally { @@ -161,7 +151,7 @@ public partial class Окне : Window } catch (Exception ex) { - Ошибки.ПоказатьСообщение(nameof(Окне), ex); + ErrorHelper.ShowMessageBox(nameof(MainWindow), ex); } } @@ -169,12 +159,12 @@ public partial class Окне : Window { try { - Launcher.LaunchFileInfoAsync(new FileInfo(Приложение.Логгер.LogfileName.ToString())) + Launcher.LaunchFileInfoAsync(new FileInfo(LauncherApp.Logger.LogfileName.ToString())) .ConfigureAwait(false); } catch (Exception ex) { - Ошибки.ПоказатьСообщение(nameof(Окне), ex); + ErrorHelper.ShowMessageBox(nameof(MainWindow), ex); } } @@ -187,7 +177,7 @@ public partial class Окне : Window } catch (Exception ex) { - Ошибки.ПоказатьСообщение(nameof(Окне), ex); + ErrorHelper.ShowMessageBox(nameof(MainWindow), ex); } } diff --git a/Млаумчерб.Клиент/зримое/VersionItemView.axaml b/Mlaumcherb.Client.Avalonia/зримое/VersionItemView.axaml similarity index 71% rename from Млаумчерб.Клиент/зримое/VersionItemView.axaml rename to Mlaumcherb.Client.Avalonia/зримое/VersionItemView.axaml index 2a629dc..077aae5 100644 --- a/Млаумчерб.Клиент/зримое/VersionItemView.axaml +++ b/Mlaumcherb.Client.Avalonia/зримое/VersionItemView.axaml @@ -2,8 +2,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:local="clr-namespace:Млаумчерб.Клиент" + xmlns:local="clr-namespace:Mlaumcherb.Client.Avalonia" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Млаумчерб.Клиент.зримое.VersionItemView"> + x:Class="Mlaumcherb.Client.Avalonia.зримое.VersionItemView"> diff --git a/Млаумчерб.Клиент/зримое/VersionItemView.axaml.cs b/Mlaumcherb.Client.Avalonia/зримое/VersionItemView.axaml.cs similarity index 79% rename from Млаумчерб.Клиент/зримое/VersionItemView.axaml.cs rename to Mlaumcherb.Client.Avalonia/зримое/VersionItemView.axaml.cs index 6ec7da5..11260ff 100644 --- a/Млаумчерб.Клиент/зримое/VersionItemView.axaml.cs +++ b/Mlaumcherb.Client.Avalonia/зримое/VersionItemView.axaml.cs @@ -1,8 +1,8 @@ using Avalonia.Controls; using Avalonia.Media; -using Млаумчерб.Клиент.классы; +using Mlaumcherb.Client.Avalonia.классы; -namespace Млаумчерб.Клиент.зримое; +namespace Mlaumcherb.Client.Avalonia.зримое; public partial class VersionItemView : ListBoxItem { @@ -12,7 +12,7 @@ public partial class VersionItemView : ListBoxItem public VersionItemView() { - throw new NotImplementedException(); + throw new Exception(); } public VersionItemView(GameVersionProps props) diff --git a/Млаумчерб.Клиент/капитал/IBMPlexMono-Regular.ttf b/Mlaumcherb.Client.Avalonia/капитал/IBMPlexMono-Regular.ttf similarity index 100% rename from Млаумчерб.Клиент/капитал/IBMPlexMono-Regular.ttf rename to Mlaumcherb.Client.Avalonia/капитал/IBMPlexMono-Regular.ttf diff --git a/Млаумчерб.Клиент/капитал/кубе.ico b/Mlaumcherb.Client.Avalonia/капитал/кубе.ico similarity index 100% rename from Млаумчерб.Клиент/капитал/кубе.ico rename to Mlaumcherb.Client.Avalonia/капитал/кубе.ico diff --git a/Млаумчерб.Клиент/капитал/лисик.gif b/Mlaumcherb.Client.Avalonia/капитал/лисик.gif similarity index 100% rename from Млаумчерб.Клиент/капитал/лисик.gif rename to Mlaumcherb.Client.Avalonia/капитал/лисик.gif diff --git a/Млаумчерб.Клиент/капитал/фоне.png b/Mlaumcherb.Client.Avalonia/капитал/фоне.png similarity index 100% rename from Млаумчерб.Клиент/капитал/фоне.png rename to Mlaumcherb.Client.Avalonia/капитал/фоне.png diff --git a/Млаумчерб.Клиент/классы/GameVersionCatalog.cs b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs similarity index 90% rename from Млаумчерб.Клиент/классы/GameVersionCatalog.cs rename to Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs index 364a07d..d1dcc8a 100644 --- a/Млаумчерб.Клиент/классы/GameVersionCatalog.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionCatalog.cs @@ -1,4 +1,4 @@ -namespace Млаумчерб.Клиент.классы; +namespace Mlaumcherb.Client.Avalonia.классы; public class GameVersionCatalog { diff --git a/Млаумчерб.Клиент/классы/GameVersionDescriptor.cs b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs similarity index 95% rename from Млаумчерб.Клиент/классы/GameVersionDescriptor.cs rename to Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs index 761a592..a5d26bd 100644 --- a/Млаумчерб.Клиент/классы/GameVersionDescriptor.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/GameVersionDescriptor.cs @@ -1,7 +1,7 @@ using System.Linq; // ReSharper disable CollectionNeverUpdated.Global -namespace Млаумчерб.Клиент.классы; +namespace Mlaumcherb.Client.Avalonia.классы; public class GameVersionDescriptor { diff --git a/Млаумчерб.Клиент/классы/JavaVersionCatalog.cs b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/JavaVersionCatalog.cs similarity index 91% rename from Млаумчерб.Клиент/классы/JavaVersionCatalog.cs rename to Mlaumcherb.Client.Avalonia/классы/Буржуазия/JavaVersionCatalog.cs index af18f96..38fde24 100644 --- a/Млаумчерб.Клиент/классы/JavaVersionCatalog.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/JavaVersionCatalog.cs @@ -1,6 +1,7 @@ using System.Runtime.InteropServices; +using Mlaumcherb.Client.Avalonia.холопы; -namespace Млаумчерб.Клиент.классы; +namespace Mlaumcherb.Client.Avalonia.классы; public class JavaVersionCatalog { @@ -54,7 +55,7 @@ public class JavaVersionCatalog } throw new PlatformNotSupportedException($"Can't download java {version.majorVersion} for your operating system. " + - $"Download it manually to directory {Пути.GetJavaRuntimeDir(version.component)}"); + $"Download it manually to directory {PathHelper.GetJavaRuntimeDir(version.component)}"); } } diff --git a/Mlaumcherb.Client.Avalonia/классы/Буржуазия/Rules.cs b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/Rules.cs new file mode 100644 index 0000000..f501f5a --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/классы/Буржуазия/Rules.cs @@ -0,0 +1,40 @@ +using Mlaumcherb.Client.Avalonia.холопы; + +namespace Mlaumcherb.Client.Avalonia.классы; + +public static class Rules +{ + public static bool Check(ICollection? rules, ICollection features) + { + if(rules is null || rules.Count == 0) + return true; + + bool allowed = false; + foreach (var r in rules) + { + if (r.os != null && !PlatformHelper.CheckOs(r.os)) + continue; + + if (r.features == null) + allowed = r.action == "allow"; + else + { + foreach (var feature in features) + { + if (r.features.TryGetValue(feature, out bool is_enabled)) + { + if (is_enabled) + { + allowed = r.action == "allow"; + } + } + } + } + + if(allowed) + break; + } + + return allowed; + } +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/ArgumentsWithPlaceholders.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/ArgumentsWithPlaceholders.cs new file mode 100644 index 0000000..e09fa99 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/ArgumentsWithPlaceholders.cs @@ -0,0 +1,31 @@ +namespace Mlaumcherb.Client.Avalonia.классы; + +public class ArgumentsWithPlaceholders +{ + protected List _raw_args = new(); + + public IEnumerable FillPlaceholders(Dictionary values) + { + foreach (var _s in _raw_args) + { + string arg = _s; + int begin = arg.IndexOf("${", StringComparison.Ordinal); + while(begin != -1) + { + int keyBegin = begin + 2; + int end = arg.IndexOf('}', keyBegin); + if (end != -1) + { + var key = arg.Substring(keyBegin, end - keyBegin); + if (!values.TryGetValue(key, out var value)) + throw new Exception($"can't find value for placeholder '{key}'"); + arg = arg.Replace("${"+ key + "}", value); + } + if(end + 1 < arg.Length) + begin = arg.IndexOf("${", end + 1, StringComparison.Ordinal); + else break; + } + yield return arg; + } + } +} diff --git a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameArguments.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameArguments.cs new file mode 100644 index 0000000..7c0f243 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameArguments.cs @@ -0,0 +1,41 @@ +using DTLib.Extensions; + +namespace Mlaumcherb.Client.Avalonia.классы; + +public class GameArguments : ArgumentsWithPlaceholders +{ + private static readonly string[] _initial_arguments = + [ + "--width", "${resolution_width}", + "--height", "${resolution_height}", + ]; + + private static readonly string[] _enabled_features = + [ + ]; + + public GameArguments(GameVersionDescriptor d) + { + _raw_args.AddRange(_initial_arguments); + + if (d.minecraftArguments is not null) + { + _raw_args.AddRange(d.minecraftArguments.SplitToList(' ', quot: '"')); + } + else if (d.arguments is not null) + { + foreach (var av in d.arguments.game) + { + if(Rules.Check(av.rules, _enabled_features)) + { + foreach (var arg in av.value) + { + if(!_raw_args.Contains(arg)) + _raw_args.Add(arg); + } + } + } + } + else throw new Exception("no game arguments specified in descriptor"); + } +} diff --git a/Млаумчерб.Клиент/классы/GameVersionProps.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs similarity index 77% rename from Млаумчерб.Клиент/классы/GameVersionProps.cs rename to Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs index e9e1494..1204df6 100644 --- a/Млаумчерб.Клиент/классы/GameVersionProps.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/GameVersionProps.cs @@ -1,4 +1,6 @@ -namespace Млаумчерб.Клиент.классы; +using Mlaumcherb.Client.Avalonia.холопы; + +namespace Mlaumcherb.Client.Avalonia.классы; public class GameVersionProps : IComparable, IEquatable { @@ -19,16 +21,13 @@ public class GameVersionProps : IComparable, IEquatable Name; diff --git a/Mlaumcherb.Client.Avalonia/классы/Пролетариат/JavaArguments.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/JavaArguments.cs new file mode 100644 index 0000000..060f8fa --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/JavaArguments.cs @@ -0,0 +1,49 @@ +namespace Mlaumcherb.Client.Avalonia.классы; + +public class JavaArguments : ArgumentsWithPlaceholders +{ + private static readonly string[] _initial_arguments = + [ + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseG1GC", + "-XX:G1NewSizePercent=20", + "-XX:G1ReservePercent=20", + "-XX:MaxGCPauseMillis=50", + "-XX:G1HeapRegionSize=32M", + "-XX:+DisableExplicitGC", + "-XX:+AlwaysPreTouch", + "-XX:+ParallelRefProcEnabled", + "-Xms${xms}M", + "-Xmx${xmx}M", + "-Dfile.encoding=UTF-8", + "-Dlog4j.configurationFile=${path}", + "-Djava.library.path=${natives_directory}", + "-Dminecraft.client.jar=${client_jar}", + "-Dminecraft.launcher.brand=${launcher_name}", + "-Dminecraft.launcher.version=${launcher_version}", + "-cp", "${classpath}" + ]; + + private static readonly string[] _enabled_features = + [ + ]; + + public JavaArguments(GameVersionDescriptor d) + { + _raw_args.AddRange(_initial_arguments); + if (d.arguments is not null) + { + foreach (var av in d.arguments.jvm) + { + if (Rules.Check(av.rules, _enabled_features)) + { + foreach (var arg in av.value) + { + if(!_raw_args.Contains(arg)) + _raw_args.Add(arg); + } + } + } + } + } +} diff --git a/Млаумчерб.Клиент/классы/Libraries.cs b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/Libraries.cs similarity index 75% rename from Млаумчерб.Клиент/классы/Libraries.cs rename to Mlaumcherb.Client.Avalonia/классы/Пролетариат/Libraries.cs index f7e23ae..4cf2f00 100644 --- a/Млаумчерб.Клиент/классы/Libraries.cs +++ b/Mlaumcherb.Client.Avalonia/классы/Пролетариат/Libraries.cs @@ -1,7 +1,7 @@ -using System.Runtime.InteropServices; -using DTLib.Extensions; +using DTLib.Extensions; +using Mlaumcherb.Client.Avalonia.холопы; -namespace Млаумчерб.Клиент.классы; +namespace Mlaumcherb.Client.Avalonia.классы; public class Libraries { @@ -20,7 +20,7 @@ public class Libraries foreach (var l in descriptor.libraries) { - if (l.rules != null && !Буржуазия.CheckRules(l.rules, enabled_features)) + if (l.rules != null && !Rules.Check(l.rules, enabled_features)) continue; if (l.natives != null) @@ -42,13 +42,7 @@ public class Libraries 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}") - }, + "arch" => span.Before("${").ToString() + PlatformHelper.GetArchOld(), _ => throw new Exception($"unknown placeholder in {nativesKey}") }; } @@ -62,7 +56,7 @@ public class Libraries continue; string urlTail = artifact.url.AsSpan().After("://").After('/').ToString(); - IOPath jarFilePath = Path.Concat(Пути.GetLibrariesDir(), urlTail); + IOPath jarFilePath = Path.Concat(PathHelper.GetLibrariesDir(), urlTail); libs.Add(new NativeLib(l.name, jarFilePath, artifact, l.extract)); } else @@ -76,7 +70,7 @@ public class Libraries continue; string urlTail = artifact.url.AsSpan().After("://").After('/').ToString(); - IOPath jarFilePath = Path.Concat(Пути.GetLibrariesDir(), urlTail); + IOPath jarFilePath = Path.Concat(PathHelper.GetLibrariesDir(), urlTail); libs.Add(new JarLib(l.name, jarFilePath, artifact)); } } diff --git a/Млаумчерб.Клиент/сеть/DataSize.cs b/Mlaumcherb.Client.Avalonia/сеть/DataSize.cs similarity index 87% rename from Млаумчерб.Клиент/сеть/DataSize.cs rename to Mlaumcherb.Client.Avalonia/сеть/DataSize.cs index 34de3cf..49077f4 100644 --- a/Млаумчерб.Клиент/сеть/DataSize.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/DataSize.cs @@ -1,4 +1,4 @@ -namespace Млаумчерб.Клиент.сеть; +namespace Mlaumcherb.Client.Avalonia.сеть; public record struct DataSize(long Bytes) { diff --git a/Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs b/Mlaumcherb.Client.Avalonia/сеть/NetworkProgressReporter.cs similarity index 85% rename from Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs rename to Mlaumcherb.Client.Avalonia/сеть/NetworkProgressReporter.cs index e8a1f39..c852651 100644 --- a/Млаумчерб.Клиент/сеть/NetworkProgressReporter.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/NetworkProgressReporter.cs @@ -1,7 +1,7 @@ -using Млаумчерб.Клиент.зримое; +using Mlaumcherb.Client.Avalonia.зримое; using Timer = DTLib.Timer; -namespace Млаумчерб.Клиент.сеть; +namespace Mlaumcherb.Client.Avalonia.сеть; public record struct DownloadProgress(DataSize Downloaded, DataSize Total, DataSize PerSecond) { @@ -54,7 +54,7 @@ public class NetworkProgressReporter : IDisposable long bytesPerSec = (_curSize - _prevSize) / (_timerDelay / 1000); _prevSize = _curSize; var p = new DownloadProgress(_curSize, _totalSize, bytesPerSec); - Приложение.Логгер.LogDebug(nameof(ReportProgress), + LauncherApp.Logger.LogDebug(nameof(ReportProgress), $"download progress {p}"); _reportProgressDelegate(p); } diff --git a/Млаумчерб.Клиент/сеть/NetworkTask.cs b/Mlaumcherb.Client.Avalonia/сеть/NetworkTask.cs similarity index 93% rename from Млаумчерб.Клиент/сеть/NetworkTask.cs rename to Mlaumcherb.Client.Avalonia/сеть/NetworkTask.cs index d3dca72..72170f2 100644 --- a/Млаумчерб.Клиент/сеть/NetworkTask.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/NetworkTask.cs @@ -1,4 +1,4 @@ -namespace Млаумчерб.Клиент.сеть; +namespace Mlaumcherb.Client.Avalonia.сеть; public class NetworkTask : IDisposable { diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs similarity index 75% rename from Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs rename to Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs index 90fbe3f..446e821 100644 --- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/AssetsDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/AssetsDownloadTaskFactory.cs @@ -2,11 +2,12 @@ using System.Net.Http; using System.Security.Cryptography; using DTLib.Extensions; -using Млаумчерб.Клиент.зримое; -using Млаумчерб.Клиент.классы; -using static Млаумчерб.Клиент.сеть.Сеть; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; +using static Mlaumcherb.Client.Avalonia.сеть.Сеть; -namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; +namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories; public class AssetsDownloadTaskFactory : INetworkTaskFactory { @@ -20,7 +21,7 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory { _descriptor = descriptor; _hasher = SHA1.Create(); - _indexFilePath = Пути.GetAssetIndexFilePath(_descriptor.assetIndex.id); + _indexFilePath = PathHelper.GetAssetIndexFilePath(_descriptor.assetIndex.id); } public async Task CreateAsync(bool checkHashes) @@ -38,9 +39,9 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory { if(!File.Exists(_indexFilePath)) { - Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading asset index to '{_indexFilePath}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading asset index to '{_indexFilePath}'"); await DownloadFile(_descriptor.assetIndex.url, _indexFilePath); - Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading asset index"); + LauncherApp.Logger.LogInfo(nameof(Сеть), "finished downloading asset index"); } string indexFileText = File.ReadAllText(_indexFilePath); @@ -101,10 +102,10 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { - Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading assets '{_descriptor.assetIndex.id}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading assets '{_descriptor.assetIndex.id}'"); ParallelOptions opt = new() { - MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок, + MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads, CancellationToken = ct }; await Parallel.ForEachAsync(_assetsToDownload, opt, @@ -113,7 +114,7 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory bool completed = false; while(!completed) { - Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading asset '{a.name}' {a.hash}"); + LauncherApp.Logger.LogDebug(nameof(Сеть), $"downloading asset '{a.name}' {a.hash}"); try { await DownloadFile(a.url, a.filePath, _ct, pr.AddBytesCount); @@ -124,13 +125,13 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory // wait on rate limit if(httpException.StatusCode == HttpStatusCode.TooManyRequests) { - Приложение.Логгер.LogDebug(nameof(Сеть), "rate limit hit"); + LauncherApp.Logger.LogDebug(nameof(Сеть), "rate limit hit"); await Task.Delay(1000, _ct); } else throw; } } }); - Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading assets '{_descriptor.assetIndex.id}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"finished downloading assets '{_descriptor.assetIndex.id}'"); } } \ No newline at end of file diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/INetworkTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/INetworkTaskFactory.cs similarity index 69% rename from Млаумчерб.Клиент/сеть/NetworkTaskFactories/INetworkTaskFactory.cs rename to Mlaumcherb.Client.Avalonia/сеть/TaskFactories/INetworkTaskFactory.cs index 184d985..8ead779 100644 --- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/INetworkTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/INetworkTaskFactory.cs @@ -1,4 +1,4 @@ -namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; +namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories; public interface INetworkTaskFactory { diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs similarity index 75% rename from Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs rename to Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs index 96532e0..e1eb1c5 100644 --- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/JavaDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/JavaDownloadTaskFactory.cs @@ -1,10 +1,11 @@ using System.Security.Cryptography; using DTLib.Extensions; -using Млаумчерб.Клиент.зримое; -using Млаумчерб.Клиент.классы; -using static Млаумчерб.Клиент.сеть.Сеть; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; +using static Mlaumcherb.Client.Avalonia.сеть.Сеть; -namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; +namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories; public class JavaDownloadTaskFactory : INetworkTaskFactory { @@ -19,7 +20,7 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory public JavaDownloadTaskFactory(GameVersionDescriptor descriptor) { _descriptor = descriptor; - _javaVersionDir = Пути.GetJavaRuntimeDir(_descriptor.javaVersion.component); + _javaVersionDir = PathHelper.GetJavaRuntimeDir(_descriptor.javaVersion.component); _hasher = SHA1.Create(); } @@ -83,38 +84,38 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { - Приложение.Логгер.LogInfo(nameof(Сеть), "started downloading java runtime " + + LauncherApp.Logger.LogInfo(nameof(Сеть), "started downloading java runtime " + $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'"); ParallelOptions opt = new() { - MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок, + MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads, CancellationToken = ct }; await Parallel.ForEachAsync(_filesToDownload, opt, async (f, _ct) => { if (f.props.downloads!.lzma != null) { - Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading lzma-compressed file '{f.path}'"); + LauncherApp.Logger.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); + LZMAHelper.Decompress(pipe, fs); } else { - Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading raw file '{f.path}'"); + LauncherApp.Logger.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}'"); + LauncherApp.Logger.LogDebug(nameof(Сеть), $"adding execute rights to file '{f.path}'"); System.IO.File.SetUnixFileMode(f.path.ToString(), UnixFileMode.UserExecute); } }); - Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading java runtime " + + LauncherApp.Logger.LogInfo(nameof(Сеть), "finished downloading java runtime " + $"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'"); } diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs similarity index 76% rename from Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs rename to Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs index 06f6689..e27b9a0 100644 --- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/LibrariesDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/LibrariesDownloadTaskFactory.cs @@ -1,11 +1,12 @@ using System.IO.Compression; using System.Security.Cryptography; using DTLib.Extensions; -using Млаумчерб.Клиент.зримое; -using Млаумчерб.Клиент.классы; -using static Млаумчерб.Клиент.сеть.Сеть; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; +using static Mlaumcherb.Client.Avalonia.сеть.Сеть; -namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; +namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories; public class LibrariesDownloadTaskFactory : INetworkTaskFactory { @@ -20,7 +21,7 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory _descriptor = descriptor; _libraries = libraries; _hasher = SHA1.Create(); - _nativesDir = Пути.GetNativeLibrariesDir(descriptor.id); + _nativesDir = PathHelper.GetNativeLibrariesDir(descriptor.id); } public Task CreateAsync(bool checkHashes) @@ -72,16 +73,16 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { - Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading libraries '{_descriptor.id}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading libraries '{_descriptor.id}'"); ParallelOptions opt = new() { - MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок, + MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads, CancellationToken = ct }; await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) => { - Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading library '{l.name}' to '{l.jarFilePath}'"); + LauncherApp.Logger.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) { @@ -101,6 +102,6 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory } }); - Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading libraries '{_descriptor.id}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"finished downloading libraries '{_descriptor.id}'"); } } \ No newline at end of file diff --git a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionFileDownloadTaskFactory.cs similarity index 69% rename from Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs rename to Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionFileDownloadTaskFactory.cs index d851420..b23fc21 100644 --- a/Млаумчерб.Клиент/сеть/NetworkTaskFactories/VersionFileDownloadTaskFactory.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/TaskFactories/VersionFileDownloadTaskFactory.cs @@ -1,10 +1,11 @@ using System.Security.Cryptography; using DTLib.Extensions; -using Млаумчерб.Клиент.зримое; -using Млаумчерб.Клиент.классы; -using static Млаумчерб.Клиент.сеть.Сеть; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.холопы; +using static Mlaumcherb.Client.Avalonia.сеть.Сеть; -namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories; +namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories; public class VersionFileDownloadTaskFactory : INetworkTaskFactory { @@ -15,7 +16,7 @@ public class VersionFileDownloadTaskFactory : INetworkTaskFactory public VersionFileDownloadTaskFactory(GameVersionDescriptor descriptor) { _descriptor = descriptor; - _filePath = Пути.GetVersionJarFilePath(_descriptor.id); + _filePath = PathHelper.GetVersionJarFilePath(_descriptor.id); _hasher = SHA1.Create(); } @@ -49,8 +50,8 @@ public class VersionFileDownloadTaskFactory : INetworkTaskFactory private async Task Download(NetworkProgressReporter pr, CancellationToken ct) { - Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading version file '{_descriptor.id}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading version file '{_descriptor.id}'"); await DownloadFile(_descriptor.downloads.client.url, _filePath, ct, pr.AddBytesCount); - Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading version file '{_descriptor.id}'"); + LauncherApp.Logger.LogInfo(nameof(Сеть), $"finished downloading version file '{_descriptor.id}'"); } } \ No newline at end of file diff --git a/Млаумчерб.Клиент/сеть/Сеть.cs b/Mlaumcherb.Client.Avalonia/сеть/Сеть.cs similarity index 89% rename from Млаумчерб.Клиент/сеть/Сеть.cs rename to Mlaumcherb.Client.Avalonia/сеть/Сеть.cs index 6e2f97a..ee34e65 100644 --- a/Млаумчерб.Клиент/сеть/Сеть.cs +++ b/Mlaumcherb.Client.Avalonia/сеть/Сеть.cs @@ -1,9 +1,8 @@ using System.Net.Http; -using System.Net.Http.Headers; -using Млаумчерб.Клиент.зримое; -using Млаумчерб.Клиент.классы; +using Mlaumcherb.Client.Avalonia.зримое; +using Mlaumcherb.Client.Avalonia.классы; -namespace Млаумчерб.Клиент.сеть; +namespace Mlaumcherb.Client.Avalonia.сеть; public static class Сеть { @@ -60,7 +59,7 @@ public static class Сеть } catch (Exception ex) { - Приложение.Логгер.LogWarn(nameof(Сеть), ex); + LauncherApp.Logger.LogWarn(nameof(Сеть), ex); } } diff --git a/Млаумчерб.Клиент/собрать b/Mlaumcherb.Client.Avalonia/собрать similarity index 100% rename from Млаумчерб.Клиент/собрать rename to Mlaumcherb.Client.Avalonia/собрать diff --git a/Млаумчерб.Клиент/Ошибки.cs b/Mlaumcherb.Client.Avalonia/холопы/ErrorHelper.cs similarity index 63% rename from Млаумчерб.Клиент/Ошибки.cs rename to Mlaumcherb.Client.Avalonia/холопы/ErrorHelper.cs index 09b19c1..cf235ab 100644 --- a/Млаумчерб.Клиент/Ошибки.cs +++ b/Mlaumcherb.Client.Avalonia/холопы/ErrorHelper.cs @@ -1,21 +1,21 @@ using Avalonia.Controls; using DTLib.Demystifier; +using Mlaumcherb.Client.Avalonia.зримое; using MsBox.Avalonia; using MsBox.Avalonia.Dto; using MsBox.Avalonia.Enums; using MsBox.Avalonia.Models; -using Млаумчерб.Клиент.зримое; -namespace Млаумчерб.Клиент; +namespace Mlaumcherb.Client.Avalonia.холопы; -public static class Ошибки +public static class ErrorHelper { - internal static void ПоказатьСообщение(string context, Exception err) - => ПоказатьСообщение(context, err.ToStringDemystified()); + internal static void ShowMessageBox(string context, Exception err) + => ShowMessageBox(context, err.ToStringDemystified()); - internal static async void ПоказатьСообщение(string context, string err) + internal static async void ShowMessageBox(string context, string err) { - Приложение.Логгер.LogError(nameof(Ошибки), err); + LauncherApp.Logger.LogError(context, err); var box = MessageBoxManager.GetMessageBoxCustom(new MessageBoxCustomParams { ButtonDefinitions = new List { new() { Name = "пон" } }, diff --git a/Млаумчерб.Клиент/LZMACompressor.cs b/Mlaumcherb.Client.Avalonia/холопы/LZMAHelper.cs similarity index 88% rename from Млаумчерб.Клиент/LZMACompressor.cs rename to Mlaumcherb.Client.Avalonia/холопы/LZMAHelper.cs index 123bb34..d7a09c4 100644 --- a/Млаумчерб.Клиент/LZMACompressor.cs +++ b/Mlaumcherb.Client.Avalonia/холопы/LZMAHelper.cs @@ -1,9 +1,9 @@ -namespace Млаумчерб.Клиент; +namespace Mlaumcherb.Client.Avalonia.холопы; /// /// https://gist.github.com/ststeiger/cb9750664952f775a341 /// -public static class LZMACompressor +public static class LZMAHelper { public static void Decompress(Stream inputStream, Stream outputStream) { diff --git a/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs b/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs new file mode 100644 index 0000000..9f8ec45 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs @@ -0,0 +1,57 @@ +using Mlaumcherb.Client.Avalonia.зримое; + +namespace Mlaumcherb.Client.Avalonia.холопы; + +public static class PathHelper +{ + public static IOPath GetRootFullPath() => + System.IO.Path.GetFullPath(new IOPath(LauncherApp.Config.minecraft_dir).ToString()); + + public static IOPath GetAssetsDir() => + Path.Concat(GetRootFullPath(), "assets"); + + public static IOPath GetAssetIndexFilePath(string id) => + Path.Concat(GetAssetsDir(), $"assets/indexes/{id}.json"); + + public static IOPath GetVersionDescriptorPath(string id) => + Path.Concat(GetVersionDir(id), id + ".json"); + + public static IOPath GetVersionsDir() => + Path.Concat(GetRootFullPath(), "versions"); + + public static IOPath GetVersionDir(string id) => + Path.Concat(GetVersionsDir(), id); + + public static IOPath GetVersionJarFilePath(string id) => + Path.Concat(GetVersionDir(id), id + ".jar"); + + public static IOPath GetLibrariesDir() => + Path.Concat(GetRootFullPath(), "libraries"); + + public static IOPath GetNativeLibrariesDir(string id) => + Path.Concat(GetVersionDir(id), "natives", PlatformHelper.GetOsAndArch()); + + public static IOPath GetJavaRuntimesDir() => + Path.Concat(GetRootFullPath(), "java"); + + + public static IOPath GetJavaRuntimeDir(string id) => + Path.Concat(GetJavaRuntimesDir(), PlatformHelper.GetOsAndArch(), id); + + public static IOPath GetJavaBinDir(string id) => + Path.Concat(GetJavaRuntimeDir(id), "bin"); + public static IOPath GetJavaExecutablePath(string id, bool debug) + { + string executable_name = "java"; + if (debug) + executable_name += "w"; + if(OperatingSystem.IsWindows()) + executable_name += ".exe"; + return Path.Concat(GetJavaBinDir(id), executable_name); + } + + public static string GetLog4jConfigFileName() => "log4j.xml"; + + public static IOPath GetLog4jConfigFilePath() => + Path.Concat(GetAssetsDir(), GetLog4jConfigFileName()); +} diff --git a/Mlaumcherb.Client.Avalonia/холопы/PlatformHelper.cs b/Mlaumcherb.Client.Avalonia/холопы/PlatformHelper.cs new file mode 100644 index 0000000..357feca --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/холопы/PlatformHelper.cs @@ -0,0 +1,55 @@ +using System.Runtime.InteropServices; +using Mlaumcherb.Client.Avalonia.классы; + +// ReSharper disable CollectionNeverUpdated.Global + +namespace Mlaumcherb.Client.Avalonia.холопы; + +public static class PlatformHelper +{ + public static bool CheckOs(Os os) => + os.name switch + { + null => true, + "osx" => OperatingSystem.IsMacOS(), + "linux" => OperatingSystem.IsLinux(), + "windows" => OperatingSystem.IsWindows(), + _ => throw new ArgumentOutOfRangeException(os.name) + } + && os.arch switch + { + null => true, + "i386" or "x86" => RuntimeInformation.OSArchitecture == Architecture.X86, + "x64" => RuntimeInformation.OSArchitecture == Architecture.X64, + "arm64" => RuntimeInformation.OSArchitecture == Architecture.Arm64, + _ => false + }; + + public static string GetOs() => + Environment.OSVersion.Platform switch + { + PlatformID.Win32NT => "windows", + PlatformID.Unix => "linux", + PlatformID.MacOSX => "osx", + _ => throw new PlatformNotSupportedException("OS not supported") + }; + + public static string GetArch() => + RuntimeInformation.OSArchitecture switch + { + Architecture.X86 => "x86", + Architecture.X64 => "x64", + Architecture.Arm64 => "arm64", + _ => throw new PlatformNotSupportedException("OS not supported") + }; + + public static string GetArchOld() => + RuntimeInformation.OSArchitecture switch + { + Architecture.X86 => "32", + Architecture.X64 => "64", + _ => throw new PlatformNotSupportedException("OS not supported") + }; + + public static string GetOsAndArch() => GetOs() + '-' + GetArch(); +} diff --git a/млаумчерб.sln b/mlaumcherb.sln similarity index 78% rename from млаумчерб.sln rename to mlaumcherb.sln index 4a36c11..dfaad27 100644 --- a/млаумчерб.sln +++ b/mlaumcherb.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Млаумчерб.Клиент", "Млаумчерб.Клиент\Млаумчерб.Клиент.csproj", "{9B9D8B05-255F-49C3-89EC-3F43A66491D3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mlaumcherb.Client.Avalonia", "Mlaumcherb.Client.Avalonia\Mlaumcherb.Client.Avalonia.csproj", "{9B9D8B05-255F-49C3-89EC-3F43A66491D3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{A3217C18-CC0D-4CE8-9C48-1BDEC1E1B333}" ProjectSection(SolutionItems) = preProject diff --git a/Млаумчерб.Клиент/Игра.cs b/Млаумчерб.Клиент/Игра.cs deleted file mode 100644 index df98a8f..0000000 --- a/Млаумчерб.Клиент/Игра.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System.Linq; -using CliWrap; -using DTLib.Extensions; -using Млаумчерб.Клиент.зримое; -using Млаумчерб.Клиент.классы; -using Млаумчерб.Клиент.сеть; -using Млаумчерб.Клиент.сеть.NetworkTaskFactories; -using static Млаумчерб.Клиент.классы.Пути; - -namespace Млаумчерб.Клиент; - -public class GameVersion -{ - private readonly GameVersionProps _props; - public string Name => _props.Name; - public IOPath WorkingDirectory { get; } - - private IOPath JavaExecutableFilePath; - - private GameVersionDescriptor descriptor; - private JavaArguments javaArgs; - private GameArguments gameArgs; - private Libraries libraries; - private CancellationTokenSource? gameCts; - private CommandTask? commandTask; - - public static async Task> GetAllVersionsAsync() - { - var propsSet = new HashSet(); - - // local descriptors - foreach (IOPath f in Directory.GetFiles(GetVersionDescriptorsDir())) - { - string name = GetVersionDescriptorName(f); - propsSet.Add(new GameVersionProps(name, null, f)); - } - - // 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; - } - - public static async Task CreateFromPropsAsync(GameVersionProps props) - { - if (!File.Exists(props.LocalDescriptorPath)) - { - if (props.RemoteDescriptorUrl is null) - throw new NullReferenceException("can't download game version descriptor '" - + props.Name + "', because RemoteDescriptorUrl is null"); - await Сеть.DownloadFile(props.RemoteDescriptorUrl, props.LocalDescriptorPath); - } - - return new GameVersion(props); - } - - private GameVersion(GameVersionProps props) - { - _props = props; - string descriptorText = File.ReadAllText(props.LocalDescriptorPath); - descriptor = JsonConvert.DeserializeObject(descriptorText) - ?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'"); - javaArgs = new JavaArguments(descriptor); - gameArgs = new GameArguments(descriptor); - libraries = new Libraries(descriptor); - WorkingDirectory = GetVersionDir(descriptor.id); - JavaExecutableFilePath = GetJavaExecutablePath(descriptor.javaVersion.component); - } - - 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(Приложение.Настройки.скачивать_жабу) - { - taskFactories.Add(new JavaDownloadTaskFactory(descriptor)); - } - //TODO: modpack - /*if (modpack != null) - { - taskFactories.Add(new ModpackDownloadTaskFactory(modpack)); - }*/ - - var networkTasks = new List(); - for (int i = 0; i < taskFactories.Count; i++) - { - var nt = await taskFactories[i].CreateAsync(checkHashes); - if (nt != null) - { - networkTasks.Add(nt); - networkTaskCreatedCallback.Invoke(nt); - } - } - - foreach (var nt in networkTasks) - { - await nt.StartAsync(); - } - - _props.IsDownloaded = true; - Приложение.Логгер.LogInfo(nameof(GameVersion), $"finished updating version {Name}"); - } - - public async Task Launch() - { - var javaArgsList = javaArgs.FillPlaceholders([]); - var gameArgsList = gameArgs.FillPlaceholders([]); - var command = Cli.Wrap(JavaExecutableFilePath.ToString()) - .WithWorkingDirectory(WorkingDirectory.ToString()) - .WithArguments(javaArgsList) - .WithArguments(gameArgsList); - Приложение.Логгер.LogInfo(nameof(GameVersion), - $"launching the game" + - "\njava: " + command.TargetFilePath + - "\nworking_dir: " + command.WorkingDirPath + - "\njava_arguments: \n\t" + javaArgsList.MergeToString("\n\t") + - "\ngame_arguments: \n\t" + gameArgsList.MergeToString("\n\t")); - gameCts = new(); - commandTask = command.ExecuteAsync(gameCts.Token); - var result = await commandTask; - Приложение.Логгер.LogInfo(nameof(GameVersion), $"game exited with code {result.ExitCode}"); - } - - public void Close() - { - gameCts?.Cancel(); - } - - public override string ToString() => Name; -} \ No newline at end of file diff --git a/Млаумчерб.Клиент/Настройки.cs b/Млаумчерб.Клиент/Настройки.cs deleted file mode 100644 index b38672f..0000000 --- a/Млаумчерб.Клиент/Настройки.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Млаумчерб.Клиент.зримое; - -namespace Млаумчерб.Клиент; - -public record Настройки -{ - public string имя_пользователя { get; set; } = ""; - public int выделенная_память_мб { get; set; } = 4096; - public string путь_к_кубачу { get; set; } = "."; - public bool запускать_полноэкранное { get; set; } = false; - public bool скачивать_жабу { get; set; } = true; - public string? последняя_запущенная_версия { get; set; } - public int максимум_параллельных_загрузок { get; set; } = 16; - - public static Настройки ЗагрузитьИзФайла(string имя_файла = "млаумчерб.настройки") - { - Приложение.Логгер.LogInfo(nameof(Настройки), $"загружаются настройки из файла '{имя_файла}'"); - if(!File.Exists(имя_файла)) - { - Приложение.Логгер.LogInfo(nameof(Настройки), "файл не существует"); - return new Настройки(); - } - - string текст = File.ReadAllText(имя_файла); - Настройки? н = JsonConvert.DeserializeObject<Настройки>(текст); - if (н == null) - { - File.Move(имя_файла, имя_файла + ".старые", true); - Ошибки.ПоказатьСообщение("Настройки", $"Не удалось прочитать настройки.\n" + - $"Сломанный файл настроек переименован в '{имя_файла}.старые'.\n" + - $"Создаётся новый файл '{имя_файла}'."); - н = new Настройки(); - н.СохранитьВФайл(); - } - - Приложение.Логгер.LogInfo(nameof(Настройки), $"настройки загружены: {н}"); - return н; - } - - public void СохранитьВФайл(string имя_файла = "млаумчерб.настройки") - { - //TODO: file backup and restore - Приложение.Логгер.LogDebug(nameof(Настройки), $"настройки сохраняются в файл '{имя_файла}'"); - var текст = JsonConvert.SerializeObject(this, Formatting.Indented); - File.WriteAllText(имя_файла, текст); - Приложение.Логгер.LogDebug(nameof(Настройки), $"настройки сохранены: {текст}"); - } -} \ No newline at end of file diff --git a/Млаумчерб.Клиент/зримое/Приложение.axaml.cs b/Млаумчерб.Клиент/зримое/Приложение.axaml.cs deleted file mode 100644 index 45e96ba..0000000 --- a/Млаумчерб.Клиент/зримое/Приложение.axaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace Млаумчерб.Клиент.зримое; - -public class Приложение : Application -{ - public static LauncherLogger Логгер = new(); - public static Настройки Настройки = new(); - - public override void Initialize() - { - Логгер.LogInfo(nameof(Приложение), "приложение запущено"); - Настройки = Настройки.ЗагрузитьИзФайла(); - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new Окне(); - } - - base.OnFrameworkInitializationCompleted(); - } -} \ No newline at end of file diff --git a/Млаумчерб.Клиент/классы/ArgumentsWithPlaceholders.cs b/Млаумчерб.Клиент/классы/ArgumentsWithPlaceholders.cs deleted file mode 100644 index 7185c9f..0000000 --- a/Млаумчерб.Клиент/классы/ArgumentsWithPlaceholders.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Млаумчерб.Клиент.классы; - -public class ArgumentsWithPlaceholders -{ - protected List raw_args = new(); - - public List FillPlaceholders(Dictionary values) - { - List result = new(); - foreach (var a in raw_args) - { - var f = a; - int begin = a.IndexOf('$'); - if (begin != -1) - { - int keyBegin = begin + 2; - int end = a.IndexOf('}', keyBegin); - if (end != -1) - { - var key = a.Substring(keyBegin, end - keyBegin); - if (!values.TryGetValue(key, out var v)) - throw new Exception($"can't find value for placeholder '{key}'"); - f = v; - } - } - result.Add(f); - } - - return result; - } -} diff --git a/Млаумчерб.Клиент/классы/GameArguments.cs b/Млаумчерб.Клиент/классы/GameArguments.cs deleted file mode 100644 index ec23bb5..0000000 --- a/Млаумчерб.Клиент/классы/GameArguments.cs +++ /dev/null @@ -1,27 +0,0 @@ -using DTLib.Extensions; - -namespace Млаумчерб.Клиент.классы; - -public class GameArguments : ArgumentsWithPlaceholders -{ - private static readonly string[] _enabled_features = - [ - "has_custom_resolution" - ]; - - public GameArguments(GameVersionDescriptor d) - { - if (d.minecraftArguments is not null) - { - raw_args.AddRange(d.minecraftArguments.SplitToList(' ', quot: '"')); - } - else if (d.arguments is not null) - { - foreach (var av in d.arguments.game) - { - if(Буржуазия.CheckRules(av.rules, _enabled_features)) - raw_args.AddRange(av.value); - } - } - } -} diff --git a/Млаумчерб.Клиент/классы/JavaArguments.cs b/Млаумчерб.Клиент/классы/JavaArguments.cs deleted file mode 100644 index 6f18c40..0000000 --- a/Млаумчерб.Клиент/классы/JavaArguments.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Млаумчерб.Клиент.классы; - -public class JavaArguments : ArgumentsWithPlaceholders -{ - private static readonly string[] _initial_arguments = - [ - - ]; - - private static readonly string[] _enabled_features = - [ - - ]; - - public JavaArguments(GameVersionDescriptor d) - { - raw_args.AddRange(_initial_arguments); - if (d.arguments is not null) - { - foreach (var av in d.arguments.jvm) - { - if(Буржуазия.CheckRules(av.rules, _enabled_features)) - raw_args.AddRange(av.value); - } - } - } -} diff --git a/Млаумчерб.Клиент/классы/Буржуазия.cs b/Млаумчерб.Клиент/классы/Буржуазия.cs deleted file mode 100644 index 6bd2388..0000000 --- a/Млаумчерб.Клиент/классы/Буржуазия.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Runtime.InteropServices; -// ReSharper disable CollectionNeverUpdated.Global - -namespace Млаумчерб.Клиент.классы; - -public static class Буржуазия -{ - public static bool CheckOs(Os os) => - os.name switch - { - null => true, - "osx" => OperatingSystem.IsMacOS(), - "linux" => OperatingSystem.IsLinux(), - "windows" => OperatingSystem.IsWindows(), - _ => throw new ArgumentOutOfRangeException(os.name) - } - && os.arch switch - { - null => true, - "i386" or "x86" => RuntimeInformation.OSArchitecture == Architecture.X86, - "x64" => RuntimeInformation.OSArchitecture == Architecture.X64, - "arm64" => RuntimeInformation.OSArchitecture == Architecture.Arm64, - _ => false - }; - - public static bool CheckRules(ICollection rules, ICollection enabled_features) - { - bool allowed = false; - foreach (var r in rules) - { - if (r.os == null || CheckOs(r.os)) - { - if (r.features != null) - { - foreach (var feature in enabled_features) - { - if(r.features.TryGetValue(feature, out bool is_enabled)) - { - if (is_enabled) - { - if (r.action != "allow") - return false; - } - } - } - } - - if (r.action == "allow") - allowed = true; - else return false; - } - } - - return allowed; - } - - public static string GetOs() => - Environment.OSVersion.Platform switch - { - PlatformID.Win32NT => "windows", - PlatformID.Unix => "linux", - PlatformID.MacOSX => "osx", - _ => throw new ArgumentOutOfRangeException(Environment.OSVersion.Platform.ToString()) - }; -} diff --git a/Млаумчерб.Клиент/классы/Пролетариат.cs b/Млаумчерб.Клиент/классы/Пролетариат.cs deleted file mode 100644 index 399532e..0000000 --- a/Млаумчерб.Клиент/классы/Пролетариат.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Млаумчерб.Клиент.классы; - -public static class Пролетариат -{ - -} \ No newline at end of file diff --git a/Млаумчерб.Клиент/классы/Пути.cs b/Млаумчерб.Клиент/классы/Пути.cs deleted file mode 100644 index 1c19999..0000000 --- a/Млаумчерб.Клиент/классы/Пути.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Млаумчерб.Клиент.зримое; - -namespace Млаумчерб.Клиент.классы; - -public static class Пути -{ - public static IOPath GetAssetIndexFilePath(string id) => - Path.Concat(Приложение.Настройки.путь_к_кубачу, $"assets/indexes/{id}.json"); - - 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(GetVersionDescriptorsDir(), Path.ReplaceRestrictedChars(name) + ".json"); - - public static IOPath GetVersionDir(string id) => - Path.Concat(Приложение.Настройки.путь_к_кубачу, "versions", id); - - public static IOPath GetVersionJarFilePath(string id) => - Path.Concat(GetVersionDir(id), id + ".jar"); - - public static IOPath GetLibrariesDir() => - Path.Concat(Приложение.Настройки.путь_к_кубачу, "libraries"); - - public static IOPath GetNativeLibrariesDir(string id) => - Path.Concat(GetVersionDir(id), "natives", Буржуазия.GetOs()); - - public static IOPath GetJavaRuntimesDir() => - Path.Concat(Приложение.Настройки.путь_к_кубачу, "java"); - - - public static IOPath GetJavaRuntimeDir(string id) => - Path.Concat(GetJavaRuntimesDir(), id); - - public static IOPath GetJavaExecutablePath(string id) => - Path.Concat(GetJavaRuntimeDir(id), "bin", - OperatingSystem.IsWindows() ? "javaw.exe" : "javaw"); -}