Browse Source

refactor my modpacks downloader, server list for monitoring and changelog now loads from server

Veloe 2 years ago
parent
commit
09ded82b80

+ 31 - 19
VeloeMinecraftLauncher/MinecraftLauncher/Downloader.cs

@@ -15,6 +15,7 @@ using VeloeMinecraftLauncher.ViewModels;
 using VeloeMinecraftLauncher.Views;
 using VeloeMinecraftLauncher.Entity.VersionManifest;
 using System.Collections.ObjectModel;
+using VeloeMinecraftLauncher.Models.Entity;
 
 namespace VeloeMinecraftLauncher.MinecraftLauncher
 {
@@ -402,7 +403,7 @@ namespace VeloeMinecraftLauncher.MinecraftLauncher
                     webClient.DownloadFileAsync(new System.Uri($"{optifineUrl}Optifine{versionJson.id}.json"), Settings.MinecraftForlderPath + "versions/" + optifinePath + "/" + optifinePath + ".json");
                     waitWhileBisy(ref webClient);
 
-                    Settings.logger.Debug("Downloading: {0}", $"Optifine{versionJson.id}.json");
+                    Settings.logger.Debug("Downloading: {0}", $"Optifine{versionJson.id}.jar");
                     DownloadingFileName($"Optifine{versionJson.id}.json");
                     webClient.DownloadFileAsync(new System.Uri($"{optifineUrl}Optifine{versionJson.id}.jar"), Settings.MinecraftForlderPath + "versions/" + optifinePath + "/" + optifinePath + ".jar");
                     waitWhileBisy(ref webClient);
@@ -452,11 +453,12 @@ namespace VeloeMinecraftLauncher.MinecraftLauncher
             return TaskStatus.RanToCompletion;
         }
 
-        public static async Task<TaskStatus> StartDownloadMcTfc(Action<string> DownloadingFileName,
+        public static async Task<TaskStatus> StartDownloadModpack(Action<string> DownloadingFileName,
             Action<bool> IsControlsEnabled,
             Action<WebClient> SetProgressBar,
             Action<WebClient> RemoveProgressBar,
             ObservableCollection<Entity.VersionManifest.Version> FilteredVersions,
+            Modpack SelectedModpack,
             bool DownloadJava = false,
             bool InstallForge = false,
             bool InstallOptifine = false,
@@ -465,13 +467,17 @@ namespace VeloeMinecraftLauncher.MinecraftLauncher
         {
             try
             {
-                var FilteredVersion = FilteredVersions.Where(x => x.id == "1.12.2").FirstOrDefault();
+                var FilteredVersion = FilteredVersions.Where(x => x.id == SelectedModpack.version).FirstOrDefault();
 
                 if (FilteredVersion is null)
                     return TaskStatus.Faulted;
 
-                InstallForge = true;
-                InstallForgeOptifine = true;
+                IsControlsEnabled(false);
+
+                InstallForge = SelectedModpack.forge;
+                InstallForgeOptifine = SelectedModpack.forgeoptifine;
+                InstallOptifine = SelectedModpack.optifine;
+                InstallFabric = SelectedModpack.fabric;
 
                 Settings.logger.Debug("Downloading {0}.json", FilteredVersion.id);
                 DownloadingFileName($"{FilteredVersion.id}.json");
@@ -490,33 +496,39 @@ namespace VeloeMinecraftLauncher.MinecraftLauncher
                     InstallFabric);
 
                 IsControlsEnabled(false);
-                var mcTfcUrl = $"https://files.veloe.link/launcher/McTFC/McTFC.zip";
 
-                if (!Directory.Exists($"{Settings.MinecraftForlderPath}versions/McTFC"))
+                string modpackUrl = null;
+
+                if (SelectedModpack.url is not null)
+                    modpackUrl = SelectedModpack.url;
+                else
+                    modpackUrl = $"https://files.veloe.link/launcher/modpacks/{SelectedModpack.name}.zip";
+
+                if (!Directory.Exists($"{Settings.MinecraftForlderPath}versions/{SelectedModpack.name}"))
                 {
-                    Settings.logger.Debug("Creating path: {0}", $"{Settings.MinecraftForlderPath}versions/McTFC");
-                    Directory.CreateDirectory($"{Settings.MinecraftForlderPath}versions/McTFC");
+                    Settings.logger.Debug("Creating path: {0}", $"{Settings.MinecraftForlderPath}versions/{SelectedModpack.name}");
+                    Directory.CreateDirectory($"{Settings.MinecraftForlderPath}versions/{SelectedModpack.name}");
                 }
 
                 WebClient webClient = new WebClient();
 
                 SetProgressBar(webClient);
 
-                Settings.logger.Debug("Downloading: {0}", "McTFC.zip");
-                DownloadingFileName("McTFC.zip");
-                webClient.DownloadFileAsync(new System.Uri(mcTfcUrl), Settings.MinecraftForlderPath + "versions/McTFC.zip");
+                Settings.logger.Debug("Downloading: {0}", $"{SelectedModpack.name}.zip");
+                DownloadingFileName($"{SelectedModpack.name}.zip");
+                webClient.DownloadFileAsync(new System.Uri(modpackUrl), Settings.MinecraftForlderPath + $"versions/{SelectedModpack.name}.zip");
                 waitWhileBisy(ref webClient);
 
-                Settings.logger.Debug("Extracting: {0}", "McTFC.zip");
-                DownloadingFileName("Unpacking McTFC.zip");
-                ZipFile.ExtractToDirectory($"{Settings.MinecraftForlderPath}versions/McTFC.zip", Settings.MinecraftForlderPath + "versions/McTFC", true);
-                File.Delete($"{Settings.MinecraftForlderPath}versions/McTFC.zip");
+                Settings.logger.Debug("Extracting: {0}", $"{SelectedModpack.name}.zip");
+                DownloadingFileName($"Unpacking {SelectedModpack.name}.zip");
+                ZipFile.ExtractToDirectory($"{Settings.MinecraftForlderPath}versions/{SelectedModpack.name}.zip", Settings.MinecraftForlderPath + $"versions/{SelectedModpack.name}", true);
+                File.Delete($"{Settings.MinecraftForlderPath}versions/{SelectedModpack.name}.zip");
 
                 RemoveProgressBar(webClient);
 
                 //Progress = 100;
-                Settings.logger.Debug("Downloading McTFC finished.");
-                DownloadingFileName("Finished McTFC!");
+                Settings.logger.Debug("Downloading {0} finished.", SelectedModpack.name);
+                DownloadingFileName($"Finished {SelectedModpack.name}!");
                 IsControlsEnabled(true);
                 webClient.Dispose();
 
@@ -524,7 +536,7 @@ namespace VeloeMinecraftLauncher.MinecraftLauncher
             catch (Exception ex)
             {
                 IsControlsEnabled(true);
-                DownloadingFileName("Error occured while downloading McTFC.");        
+                DownloadingFileName($"Error occured while downloading {SelectedModpack.name}.");        
                 var message = ex.Message;
                 var stackTrace = ex.StackTrace;
                 Settings.logger.Error(ex.Message);

+ 9 - 0
VeloeMinecraftLauncher/Models/Entity/Changelog.cs

@@ -0,0 +1,9 @@
+namespace VeloeMinecraftLauncher.Models.Entity;
+public class Changelog
+{
+    public string title { get; set; }
+    public string text { get; set; }
+    public string version { get; set; }
+    public string date { get; set; }
+}
+

+ 18 - 0
VeloeMinecraftLauncher/Models/Entity/Modpack.cs

@@ -0,0 +1,18 @@
+namespace VeloeMinecraftLauncher.Models.Entity;
+public class Modpack
+{
+    public string name { get; set; }
+    public string version { get; set; }
+    public bool forge { get; set; } = false;
+    public bool forgeoptifine { get; set; } = false;
+    public bool optifine { get; set; } = false;
+    public bool fabric { get; set; } = false;
+    public string url { get; set; }
+    public int revision { get; set; }
+
+    public override string ToString()
+    {
+        return name;
+    }
+}
+

+ 2 - 3
VeloeMinecraftLauncher/VeloeMinecraftLauncher.csproj

@@ -9,8 +9,8 @@
     <DebugType>embedded</DebugType>
     <StartupObject>VeloeMinecraftLauncher.Program</StartupObject>
     <PlatformTarget>x64</PlatformTarget>
-    <AssemblyVersion>1.1.1.5</AssemblyVersion>
-    <FileVersion>1.1.1.5</FileVersion>
+    <AssemblyVersion>1.1.2.0</AssemblyVersion>
+    <FileVersion>1.1.2.0</FileVersion>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
     <NoWarn>NU1605</NoWarn>
@@ -20,7 +20,6 @@
     <NoWarn>NU1605</NoWarn>
   </PropertyGroup>
   <ItemGroup>
-    <Folder Include="Models\" />
     <AvaloniaResource Include="Assets\**" />
     <None Remove=".gitignore" />
   </ItemGroup>

+ 174 - 146
VeloeMinecraftLauncher/ViewModels/MainWindowViewModel.cs

@@ -20,6 +20,11 @@ using Avalonia.Threading;
 using System.Net;
 using System.IO.Compression;
 using ReactiveUI.Validation.Extensions;
+using Avalonia.Media;
+using Avalonia.Data;
+using ReactiveUI.Validation.Helpers;
+using System.Collections.Generic;
+using VeloeMinecraftLauncher.Models.Entity;
 
 namespace VeloeMinecraftLauncher.ViewModels
 {
@@ -92,84 +97,90 @@ namespace VeloeMinecraftLauncher.ViewModels
                 }
                 try
                 {
-                  
-                logger.Debug("Connecting to WebSoket");
-                //conection to my servers 
-                connection = new HubConnectionBuilder()
-                    .WithUrl("https://monitor.veloe.link/hubs/data")
-                    .WithAutomaticReconnect()
-                    .Build();
-
-                Func<Exception, Task> reconnecting = ex => Task.Run(() =>
+                    var changelog = Downloader.DownloadAndDeserializeJsonData<List<Changelog>>("https://files.veloe.link/launcher/changelog.json");
+
+                    if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+                    {
+                        Dispatcher.UIThread.InvokeAsync(() =>
+                        {
+                            var stackpanel = desktop.MainWindow.GetControl<StackPanel>("ChangeLogStackPanel");
+                            foreach (var version in changelog)
+                            {
+                                if (version.title is not null)
+                                {
+                                    stackpanel.Children.Add(new TextBlock()
+                                    {
+                                        Text = version.title,
+                                        VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
+                                        TextWrapping = TextWrapping.Wrap,
+                                        FontSize = 16
+                                    });
+                                }
+                                stackpanel.Children.Add(new TextBlock()
+                                {
+                                    Text = version.text,
+                                    VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
+                                    TextWrapping = TextWrapping.Wrap
+                                });
+                            }
+                        });
+
+                    }
+                }
+                catch (Exception ex)
                 {
-                    logger.Warning("Reconnecting to WebCoket...");
-                });
-                Func<string, Task> reconnected = str => Task.Run(() =>
+                    OpenErrorWindow(ex);
+                }
+                try
                 {
-                    logger.Warning("Reconnected to WebCoket.");
-                    connection.InvokeAsync("ConnectToGroup", "McTFC");
-                    connection.InvokeAsync("ConnectToGroup", "McVanilla");
-                    connection.InvokeAsync("ConnectToGroup", "McTech");
-                });
+                  
+                    logger.Debug("Connecting to WebSoket");
+                        //conection to my servers 
 
-                connection.Reconnecting += reconnecting;
-                connection.Reconnected += reconnected;
+                    var serverNames = Downloader.DownloadAndDeserializeJsonData<List<string>>("https://files.veloe.link/launcher/servers.json");
 
-                connection.On<string>("UpdateMcTFC", (message) =>
-                {
-                    McStatus status = JsonSerializer.Deserialize<McStatus>(message);
-                    McTfcBlock = $"McTFC: Online";
-                    McTfcPlayersBlock = $"{status.NumPlayers}/{status.MaxPlayers}";
-                    McTfcTip = String.Empty;
-                    if (UInt16.Parse(status.NumPlayers) > 0)
-                        foreach (var player in status.Players)
+                    connection = new HubConnectionBuilder()
+                        .WithUrl("https://monitor.veloe.link/hubs/data")
+                        .WithAutomaticReconnect()
+                        .Build();
+                
+                    Func<Exception, Task> reconnecting = ex => Task.Run(() =>
+                    {
+                        logger.Warning("Reconnecting to WebCoket...");
+                    });
+                    Func<string, Task> reconnected = str => Task.Run(() =>
+                    {
+                        logger.Warning("Reconnected to WebCoket.");
+                        foreach (var server in serverNames)
                         {
-                            McTfcTip += player;
-                            McTfcTip += "\n";
+                            connection.InvokeAsync("ConnectToGroup", server);
                         }
-                    else
-                        McTfcTip = "No players.";
-                    mcTfcTimer.Stop();
-                    mcTfcTimer.Start();
+                    });
 
-                    //ConsoleText += message;
-                });
+                    connection.Reconnecting += reconnecting;
+                    connection.Reconnected += reconnected;
 
-                connection.On<string>("UpdateMcVanilla", (message) =>
-                {
-                    McStatus status = JsonSerializer.Deserialize<McStatus>(message);
-                    McVanillaBlock = $"McVanilla: Online";
-                    McVanillaPlayersBlock = $"{status.NumPlayers}/{status.MaxPlayers}";
-                    mcTechTimer.Stop();
-                    mcTechTimer.Start();
-                    //ConsoleText += message;
-                });
-
-                connection.On<string>("UpdateMcTech", (message) =>
-                {
-                    McStatus status = JsonSerializer.Deserialize<McStatus>(message);
-                    McTechBlock = $"McTech: Online {status.NumPlayers}/{status.MaxPlayers}";
-                    McTechPlayersBlock = $"{status.NumPlayers}/{status.MaxPlayers}";
-                    mcVanillaTimer.Stop();
-                    mcVanillaTimer.Start();
-                    //ConsoleText += message;
-                });
+                    if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+                    {
+                        Dispatcher.UIThread.InvokeAsync(() =>
+                        {
+                            var stackpanel = desktop.MainWindow.GetControl<StackPanel>("ServersStackPanel");
+                            foreach (var server in serverNames)
+                                stackpanel.Children.Add(CreateServerPanel(server));
+                        });
 
-                mcTfcTimer.Elapsed += OnTimedEventMcTfc;
-                mcTfcTimer.Start();
-                mcTechTimer.Elapsed += OnTimedEventMcTech;
-                mcTechTimer.Start();
-                mcVanillaTimer.Elapsed += OnTimedEventMcVanilla;
-                mcVanillaTimer.Start();
+                    }
 
-                connection.StartAsync();
+                    connection.StartAsync();
 
-                connection.InvokeAsync("ConnectToGroup", "McTFC");
-                connection.InvokeAsync("ConnectToGroup", "McVanilla");
-                connection.InvokeAsync("ConnectToGroup", "McTech");
+                    foreach (var server in serverNames)
+                    {
+                        connection.InvokeAsync("ConnectToGroup", server);
+                    }
+                    logger.Debug("Connected to WebSoket");
 
-                consoleOutputTimer.Elapsed += UpdateConsoleOutput;
-                consoleOutputTimer.AutoReset = false;
+                    consoleOutputTimer.Elapsed += UpdateConsoleOutput;
+                    consoleOutputTimer.AutoReset = false;
                 }
                 catch (Exception ex)
                 {
@@ -179,9 +190,6 @@ namespace VeloeMinecraftLauncher.ViewModels
 
         }
 
-        System.Timers.Timer mcTfcTimer = new System.Timers.Timer(30000);
-        System.Timers.Timer mcTechTimer = new System.Timers.Timer(30000);
-        System.Timers.Timer mcVanillaTimer = new System.Timers.Timer(30000);
         System.Timers.Timer consoleOutputTimer = new(250);
 
         private HubConnection connection;
@@ -195,15 +203,6 @@ namespace VeloeMinecraftLauncher.ViewModels
         private bool isUpdateAvailable = false;
         private string settingsButton = "Settings";
         private string startButtonOutput;
-        private string mcTfcBlock = "Wait for update...";
-        private string mcTechBlock = "Wait for update...";
-        private string mcVanillaBlock = "Wait for update...";
-        private string mcTfcPlayersBlock = String.Empty;
-        private string mcTechPlayersBlock = String.Empty;
-        private string mcVanillaPlayersBlock = String.Empty;
-        private string mcTfcTip = "No players.";
-        private string mcTechTip = "No players.";
-        private string mcVanillaTip = "No players.";
 
         ILogger logger;
 
@@ -321,63 +320,9 @@ namespace VeloeMinecraftLauncher.ViewModels
             set => this.RaiseAndSetIfChanged(ref isUpdateAvailable, value);
         }
 
-        public string McTfcBlock
-        {
-            get => mcTfcBlock;
-            set => this.RaiseAndSetIfChanged(ref mcTfcBlock, value);
-        }
-
-        public string McTechBlock
-        {
-            get => mcTechBlock;
-            set => this.RaiseAndSetIfChanged(ref mcTechBlock, value);
-        }
-
-        public string McVanillaBlock
-        {
-            get => mcVanillaBlock;
-            set => this.RaiseAndSetIfChanged(ref mcVanillaBlock, value);
-        }
-
-        public string McTfcPlayersBlock
-        {
-            get => mcTfcPlayersBlock;
-            set => this.RaiseAndSetIfChanged(ref mcTfcPlayersBlock, value);
-        }
-
-        public string McTechPlayersBlock
-        {
-            get => mcTechPlayersBlock;
-            set => this.RaiseAndSetIfChanged(ref mcTechPlayersBlock, value);
-        }
-
-        public string McVanillaPlayersBlock
-        {
-            get => mcVanillaPlayersBlock;
-            set => this.RaiseAndSetIfChanged(ref mcVanillaPlayersBlock, value);
-        }
-
-        public string McTfcTip
-        {
-            get => mcTfcTip;
-            set => this.RaiseAndSetIfChanged(ref mcTfcTip, value);
-        }
-
-        public string McTechTip
-        {
-            get => mcTechTip;
-            set => this.RaiseAndSetIfChanged(ref mcTechTip, value);
-        }
-
-        public string McVanillaTip
-        {
-            get => mcVanillaTip;
-            set => this.RaiseAndSetIfChanged(ref mcVanillaTip, value);
-        }
-
         public int ConsoleTextCaretIndex
         {
-            get { return int.MaxValue; }
+            get => int.MaxValue;
             set => this.RaisePropertyChanged(nameof(ConsoleTextCaretIndex));
         }
 
@@ -804,6 +749,70 @@ namespace VeloeMinecraftLauncher.ViewModels
 
         }
 
+        private Panel CreateServerPanel(string name)
+        {
+            ServerPanelModel serverPanelModel = new() { Name = name , Status = "Wait for update...", Tip = "No players.", Players = string.Empty};
+
+            Panel panel = new Panel() 
+            { 
+                VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top, 
+                HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, 
+                Height = 75, 
+                Width = 150, 
+                Margin = Avalonia.Thickness.Parse("0 0 10 10")
+            };
+
+            panel.Children.Add(new Border() 
+            { 
+                Background = Brushes.Black, 
+                Opacity = 0.2, 
+                CornerRadius = Avalonia.CornerRadius.Parse("15") 
+            });
+
+            var tip = new ToolTip()
+            {
+                Content = new TextBlock() {TextWrapping = TextWrapping.Wrap, [!TextBlock.TextProperty] = new Binding { Source = serverPanelModel,Path = nameof(serverPanelModel.Tip) } }
+            };
+
+            ToolTip.SetTip(panel, tip);
+
+            StackPanel textPanel = new() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center };
+
+            textPanel.Children.Add(new TextBlock() { VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, [!TextBlock.TextProperty] = new Binding { Source = serverPanelModel, Path = nameof(serverPanelModel.Status) } });
+            textPanel.Children.Add(new TextBlock() { FontSize = 20, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, [!TextBlock.TextProperty] = new Binding { Source = serverPanelModel, Path = nameof(serverPanelModel.Players) } });
+            
+            panel.Children.Add(textPanel);
+
+            System.Timers.Timer timeoutTimer = new System.Timers.Timer(30000);
+
+            timeoutTimer.Elapsed += (Object source, ElapsedEventArgs e) => 
+            { 
+                serverPanelModel.Status = $"{serverPanelModel.Name}: Offline"; 
+                serverPanelModel.Players = string.Empty; 
+            };
+            timeoutTimer.Start();
+
+            connection.On<string>($"Update{serverPanelModel.Name}", (message) =>
+            {
+                McStatus status = JsonSerializer.Deserialize<McStatus>(message);
+                serverPanelModel.Status = $"{serverPanelModel.Name}: Online";
+                serverPanelModel.Players = $"{status.NumPlayers}/{status.MaxPlayers}";
+                serverPanelModel.Tip = String.Empty;
+                if (UInt16.Parse(status.NumPlayers) > 0)
+                    foreach (var player in status.Players)
+                    {
+                        serverPanelModel.Tip += player;
+                        serverPanelModel.Tip += "\n";
+                    }
+                else
+                    serverPanelModel.Tip = "No players.";
+                timeoutTimer.Stop();
+                timeoutTimer.Start();
+            });
+
+            return panel;
+        }
+
         private void OpenErrorWindow(Exception ex)
         {
             var message = ex.Message;
@@ -833,19 +842,6 @@ namespace VeloeMinecraftLauncher.ViewModels
                 }
             });
         }
-
-        private void OnTimedEventMcTfc(Object source, ElapsedEventArgs e)
-        {
-            McTfcBlock = "McTFC: Offline";
-        }
-        private void OnTimedEventMcTech(Object source, ElapsedEventArgs e)
-        {
-            McTechBlock = "McTech: Offline";
-        }
-        private void OnTimedEventMcVanilla(Object source, ElapsedEventArgs e)
-        {
-            McVanillaBlock = "McVanilla: Offline";
-        }
     }
 
     public class LatestLauncherVersion
@@ -865,5 +861,37 @@ namespace VeloeMinecraftLauncher.ViewModels
             return version;
         }
     }
+
+    public class ServerPanelModel: ReactiveValidationObject
+    {
+        private string _name;
+        private string _status;
+        private string _tip;
+        private string _players;
+
+        public string Name
+        { 
+            get => _name;
+            set => this.RaiseAndSetIfChanged(ref _name, value);            
+        }
+
+        public string Status
+        {
+            get => _status;
+            set => this.RaiseAndSetIfChanged(ref _status, value);
+        }
+
+        public string Tip
+        {
+            get => _tip;
+            set => this.RaiseAndSetIfChanged(ref _tip, value);
+        }
+
+        public string Players
+        {
+            get => _players;
+            set => this.RaiseAndSetIfChanged(ref _players, value);
+        }
+    }
    
 }

+ 34 - 8
VeloeMinecraftLauncher/ViewModels/VersionsDownloaderViewModel.cs

@@ -1,13 +1,17 @@
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Threading;
+using DynamicData;
 using ReactiveUI;
 using System;
+using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Linq;
 using System.Net;
 using System.Net.Http;
 using System.Threading.Tasks;
 using VeloeMinecraftLauncher.Entity.VersionManifest;
 using VeloeMinecraftLauncher.MinecraftLauncher;
+using VeloeMinecraftLauncher.Models.Entity;
 using VeloeMinecraftLauncher.Views;
 
 namespace VeloeMinecraftLauncher.ViewModels
@@ -33,7 +37,9 @@ namespace VeloeMinecraftLauncher.ViewModels
         Serilog.ILogger logger;
 
         ObservableCollection<Entity.VersionManifest.Version> filteredVersions;
+        ObservableCollection<Modpack> modpackVersions;
         Entity.VersionManifest.Version filteredVersion;
+        Modpack selectedModpack;
         VersionManifest versionManifest;
 
         public VersionsDownloaderViewModel()
@@ -45,10 +51,15 @@ namespace VeloeMinecraftLauncher.ViewModels
                     logger = Settings.logger;
                     if (FilteredVersions is null)
                     {
-                        FilteredVersions = new ObservableCollection<Entity.VersionManifest.Version>();
+                        FilteredVersions = new();
+                    }
+                    if (ModpackVersions is null)
+                    {
+                        ModpackVersions = new();
                     }
                     logger.Debug("Getting versionManifest.json");
                     versionManifest = Downloader.DownloadAndDeserializeJsonData<VersionManifest>("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json");
+                    ModpackVersions.AddRange(Downloader.DownloadAndDeserializeJsonData<List<Modpack>>("https://files.veloe.link/launcher/modpacks.json"));
                     logger.Debug("Updating available versions to download.");
                     updateList();
                 });
@@ -162,12 +173,24 @@ namespace VeloeMinecraftLauncher.ViewModels
             }
         }
 
-        public ObservableCollection<Entity.VersionManifest.Version>FilteredVersions
+        public Modpack SelectedModpack
+        {
+            get => selectedModpack;
+            set => this.RaiseAndSetIfChanged(ref selectedModpack, value);
+        }
+
+        public ObservableCollection<Entity.VersionManifest.Version> FilteredVersions
         {
             get => filteredVersions; 
             set => this.RaiseAndSetIfChanged(ref filteredVersions, value);
         }
 
+        public ObservableCollection<Models.Entity.Modpack> ModpackVersions
+        {
+            get => modpackVersions;
+            set => this.RaiseAndSetIfChanged(ref modpackVersions, value);
+        }
+
         public string StartButton
         {
             get => startButton; 
@@ -306,19 +329,22 @@ namespace VeloeMinecraftLauncher.ViewModels
             return TaskStatus.RanToCompletion; });
         }
       
-        public async void OnStartMcTfcBunttonClick()
+        public async void OnStartModpackBunttonClick()
         {
-            Task.Run(() => Downloader.StartDownloadMcTfc(
+            FilteredVersion = FilteredVersions.Where(x => x.id == SelectedModpack.version).FirstOrDefault();
+
+            Task.Run(() => Downloader.StartDownloadModpack(
                 value => DownloadingFileName = value,
                 value => IsControlsEnabled = value,
                 value => value.DownloadProgressChanged += WebClient_DownloadProgressChanged,
                 value => value.DownloadProgressChanged -= WebClient_DownloadProgressChanged,
                 FilteredVersions,
+                SelectedModpack,
                 DownloadJava,
-                InstallForge,
-                InstallOptifine,
-                InstallForgeOptifine,
-                InstallFabric));
+                InstallFabric = SelectedModpack.fabric,
+                InstallForge = SelectedModpack.forge,
+                InstallOptifine = SelectedModpack.optifine,
+                InstallForgeOptifine = SelectedModpack.forgeoptifine));
         }
 
         private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)

+ 3 - 137
VeloeMinecraftLauncher/Views/MainWindow.axaml

@@ -77,99 +77,9 @@
 						Header="Servers"
 						VerticalContentAlignment="Center">
 						<StackPanel
+							Name = "ServersStackPanel"
 							Orientation="Horizontal"
 							HorizontalAlignment="Stretch">
-							<Panel
-								VerticalAlignment="Top"
-								HorizontalAlignment="Stretch"
-								Height="75" Width="150"
-								Margin="0 0 10 10">
-								<Border Background="Black" Opacity="0.2" CornerRadius="15" />
-								
-								<ToolTip.Tip>
-									<TextBlock
-										TextWrapping="Wrap"
-										Text="{Binding McTfcTip}">
-									</TextBlock>
-								</ToolTip.Tip>
-								<StackPanel
-									HorizontalAlignment="Center"
-									VerticalAlignment="Center">
-									<TextBlock
-										Text="{Binding McTfcBlock}"
-										VerticalAlignment="Center"
-										HorizontalAlignment="Center">
-										McTFC
-									</TextBlock>
-									<TextBlock
-										Text="{Binding McTfcPlayersBlock}"
-										FontSize="20"
-										VerticalAlignment="Center"
-										HorizontalAlignment="Center">
-									</TextBlock>									
-								</StackPanel>
-								
-							</Panel>
-							<Panel
-								VerticalAlignment="Top"
-								HorizontalAlignment="Stretch"
-								Height="75"
-								Width="150"
-								Margin="0 0 10 10">
-								<Border Background="Black" Opacity="0.2" CornerRadius="15" />
-								<ToolTip.Tip>
-									<TextBlock
-										TextWrapping="Wrap"
-										Text="{Binding McTechTip}">
-									</TextBlock>
-								</ToolTip.Tip>
-								<StackPanel
-									HorizontalAlignment="Center"
-									VerticalAlignment="Center">
-									<TextBlock
-										Text="{Binding McTechBlock}"
-										VerticalAlignment="Center"
-										HorizontalAlignment="Center">
-										McTech
-									</TextBlock>
-									<TextBlock
-										Text="{Binding McTechPlayersBlock}"
-										FontSize="20"
-										VerticalAlignment="Center"
-										HorizontalAlignment="Center">
-									</TextBlock>
-								</StackPanel>
-							</Panel>
-							<Panel
-								VerticalAlignment="Top"
-								HorizontalAlignment="Stretch"
-								Height="75"
-								Width="150"
-								Margin="0 0 10 10">
-								<Border Background="Black" Opacity="0.2" CornerRadius="15" />
-								<ToolTip.Tip>
-									<TextBlock
-										TextWrapping="Wrap"
-										Text="{Binding McVanillaTip}">
-									</TextBlock>
-								</ToolTip.Tip>
-								<StackPanel
-									HorizontalAlignment="Center"
-									VerticalAlignment="Center">
-									<TextBlock
-										Text="{Binding McVanillaBlock}"
-										VerticalAlignment="Center"
-										HorizontalAlignment="Center">
-										McVanilla
-									</TextBlock>
-									<TextBlock
-										Text="{Binding McVanillaPlayersBlock}"
-										FontSize="20"
-										VerticalAlignment="Center"
-										HorizontalAlignment="Center">
-									</TextBlock>
-								</StackPanel>
-							</Panel>
 						</StackPanel>						
 					</TabItem>
 					<TabItem>
@@ -214,54 +124,10 @@
 								VerticalAlignment="Stretch"
 								HorizontalAlignment="Stretch">
 								<StackPanel
+									Name="ChangeLogStackPanel"
 									Width="760"
 									Margin="5,5,5,5">
-									<TextBlock
-										VerticalAlignment="Stretch"
-										TextWrapping="Wrap"
-										ScrollViewer.VerticalScrollBarVisibility="Visible"
-										Text="При проблемах отправьте мне логи.&#10; ">
-									</TextBlock>
-									<TextBlock
-										FontSize="16"
-										Text="v 1.1.1.5 (win x64, linux x64)">
-									</TextBlock>
-									<TextBlock
-										VerticalAlignment="Stretch"
-										TextWrapping="Wrap"
-										ScrollViewer.VerticalScrollBarVisibility="Visible"
-										Text="Обновление интерфейса.&#10;Добавлена поддержка установщиков Fabric.&#10;Добавлена проверка файлов ванильной версии игры перед запуском.&#10;Добавлены настройки для логов игры.&#10;Добавлена проверка на пустое значение имени игрока.&#10;Добавлено открытие окна с ошибкой при неудачном запуске игры или ее краше.&#10;Исправление открытия диалогов на linux.&#10;Исправление диалога для выбора Java.&#10;Исправление ошибки при скачивании файла с ассетами игры.&#10;Исправлена работа с forge на новых версиях (1.18,1.19)&#10;Немного ускорена скорость загрузки файлов.&#10;Собрано на .NET 7.&#10;Известные проблемы: ошибка при скачивании версий ниже 1.7.2.&#10;&#10;">
-									</TextBlock>
-									<TextBlock
-										FontSize="16"
-										Text="v 1.1.0.0 (win x64, linux x64)">
-									</TextBlock>
-									<TextBlock
-										VerticalAlignment="Stretch"
-										TextWrapping="Wrap"
-										ScrollViewer.VerticalScrollBarVisibility="Visible"
-										Text="Обновление интерфейса.&#10;Добавлены сообщения об ошибках при обработке исключений.&#10;Улучшено логгирование.&#10;Исправлена ошибка вылетов при отсутвии интернета.&#10;Установщики Optifine теперь работают кооректно, добавляя рабочий конфиг для запуска.&#10;Updater теперь обновляется при запуске лаунчера. (Windows only)&#10; ">
-									</TextBlock>
-									<TextBlock
-										FontSize="16"
-										Text="v 1.0.0.2 (win x64)">
-									</TextBlock>
-									<TextBlock
-										VerticalAlignment="Stretch"
-										TextWrapping="Wrap"
-										ScrollViewer.VerticalScrollBarVisibility="Visible"
-										Text="Консоль и Changelog теперь выводится в TextBlock вместо TextBox.&#10;Лог игры игры можно выводить в консоль, однако сохраняться в файл лога лаунчера он не будет.&#10;Последняя запущенная версия теперь сохраняется в настройках.&#10;Для выбора папки с игрой и java можно воспользоваться диалоговым окном проводника.&#10;Добавлены отступы к элементам на диалоговых окнах настроек и весий.&#10;Исправлена проблема при загрузке клиентa McTFC.&#10;Исправлена проблема с выбором певрого элемента из скачанных версий.&#10;Добавлен валидатор на поле ввода максимальной оперативной памяти.&#10; ">
-									</TextBlock>
-									<TextBlock
-										FontSize="16"
-										Text="v 1.0.0.1 (win x64)">
-									</TextBlock>
-									<TextBlock
-										VerticalAlignment="Stretch"
-										TextWrapping="Wrap"
-										ScrollViewer.VerticalScrollBarVisibility="Visible"
-										Text="Первый рабочий билд.&#10;">
-									</TextBlock>
+									
 								</StackPanel>
 							</ScrollViewer>
 						</Panel>

+ 2 - 2
VeloeMinecraftLauncher/Views/VersionsDownloader.axaml

@@ -51,8 +51,8 @@
 				<CheckBox IsChecked="{Binding InstallForgeOptifine}" IsVisible="{Binding InstallForgeOptifineVisible}" IsEnabled="{Binding IsControlsEnabled}">Install Optifine (Mod)</CheckBox>
 				<CheckBox IsChecked="{Binding InstallOptifine}" IsVisible="{Binding InstallOptifineVisible}" IsEnabled="{Binding IsControlsEnabled}">Install Optifine (Vanilla)</CheckBox>
 				<Button Content="{Binding StartButton}" HorizontalAlignment="Center" Command="{Binding OnStartBunttonClick}" IsEnabled="{Binding IsControlsEnabled}"></Button>
-				<Button Content="Install McTFC Client" HorizontalAlignment="Center" Command="{Binding OnStartMcTfcBunttonClick}" IsEnabled="{Binding IsControlsEnabled}"></Button>
-				<Button Content="Install McTech Client" HorizontalAlignment="Center" Command="{Binding OnClickCommand}" IsEnabled="{Binding IsControlsEnabled}"></Button>
+				<ComboBox Items="{Binding ModpackVersions}" PlaceholderText="Select pack" SelectedItem="{Binding SelectedModpack}" IsEnabled="{Binding IsControlsEnabled}" HorizontalAlignment="Stretch"></ComboBox>
+				<Button Content="Install Modpack" HorizontalAlignment="Center" Command="{Binding OnStartModpackBunttonClick}" IsEnabled="{Binding IsControlsEnabled}"></Button>
 				<ProgressBar Value="{Binding Progress}" ShowProgressText="true"></ProgressBar>
 				<TextBlock Text="{Binding DownloadingFileName}"></TextBlock>
 			</StackPanel>