Veloe 3 年 前
コミット
f2fd143672
32 ファイル変更3120 行追加0 行削除
  1. 14 0
      VeloeLauncherUpdater/LatestLauncherVersion.cs
  2. 86 0
      VeloeLauncherUpdater/Program.cs
  3. 16 0
      VeloeLauncherUpdater/VeloeLauncherUpdater.csproj
  4. 31 0
      VeloeMinecraftLauncher.sln
  5. 454 0
      VeloeMinecraftLauncher/.gitignore
  6. 12 0
      VeloeMinecraftLauncher/App.axaml
  7. 29 0
      VeloeMinecraftLauncher/App.axaml.cs
  8. BIN
      VeloeMinecraftLauncher/Assets/avalonia-logo.ico
  9. 42 0
      VeloeMinecraftLauncher/MinecraftLauncher/Downloader.cs
  10. 46 0
      VeloeMinecraftLauncher/MinecraftLauncher/EventSink.cs
  11. 115 0
      VeloeMinecraftLauncher/MinecraftLauncher/Settings.cs
  12. 156 0
      VeloeMinecraftLauncher/MinecraftLauncher/StartCommandBuilder.cs
  13. 24 0
      VeloeMinecraftLauncher/Models/Entity/Assets/Asset.cs
  14. 29 0
      VeloeMinecraftLauncher/Models/Entity/LauncherProfiles.cs
  15. 23 0
      VeloeMinecraftLauncher/Models/Entity/McStatus/McStatus.cs
  16. 191 0
      VeloeMinecraftLauncher/Models/Entity/Version/Version.cs
  17. 14 0
      VeloeMinecraftLauncher/Models/Entity/VesrionManifest/Latest.cs
  18. 24 0
      VeloeMinecraftLauncher/Models/Entity/VesrionManifest/Version.cs
  19. 14 0
      VeloeMinecraftLauncher/Models/Entity/VesrionManifest/VersionManifest.cs
  20. 24 0
      VeloeMinecraftLauncher/Program.cs
  21. 35 0
      VeloeMinecraftLauncher/VeloeMinecraftLauncher.csproj
  22. 30 0
      VeloeMinecraftLauncher/ViewLocator.cs
  23. 622 0
      VeloeMinecraftLauncher/ViewModels/MainWindowViewModel.cs
  24. 170 0
      VeloeMinecraftLauncher/ViewModels/SettingsWindowViewModel.cs
  25. 695 0
      VeloeMinecraftLauncher/ViewModels/VersionsDownloaderViewModel.cs
  26. 16 0
      VeloeMinecraftLauncher/ViewModels/ViewModelBase.cs
  27. 92 0
      VeloeMinecraftLauncher/Views/MainWindow.axaml
  28. 12 0
      VeloeMinecraftLauncher/Views/MainWindow.axaml.cs
  29. 47 0
      VeloeMinecraftLauncher/Views/SettingsWindow.axaml
  30. 12 0
      VeloeMinecraftLauncher/Views/SettingsWindow.axaml.cs
  31. 33 0
      VeloeMinecraftLauncher/Views/VersionsDownloader.axaml
  32. 12 0
      VeloeMinecraftLauncher/Views/VersionsDownloader.axaml.cs

+ 14 - 0
VeloeLauncherUpdater/LatestLauncherVersion.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeLauncherUpdater
+{
+    public class LatestLauncherVersion
+    {
+        public string latest { get; set; }
+        public string url { get; set; }
+    }
+}

+ 86 - 0
VeloeLauncherUpdater/Program.cs

@@ -0,0 +1,86 @@
+// See https://aka.ms/new-console-template for more information
+using Serilog;
+using Serilog.Events;
+using System.Diagnostics;
+using System.IO.Compression;
+using System.Net;
+using System.Text.Json;
+using VeloeLauncherUpdater;
+
+Console.WriteLine("Hello, World!");
+
+var logger = new LoggerConfiguration()
+    .MinimumLevel.Debug()
+    .WriteTo.File("update.log", LogEventLevel.Debug)// restricted... is Optional
+    .CreateLogger();
+
+try
+{
+    FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo("VeloeMinecraftLauncher.exe");
+
+    LatestLauncherVersion latestLauncherInfo;
+
+    using (var webClient = new HttpClient())
+    {
+        var jsonData = string.Empty;
+        try
+        {
+            jsonData = webClient.GetStringAsync("https://files.veloe.link/launcher/update/versions.json").Result;
+        }
+        catch (Exception) 
+        {
+            logger.Error("Error occured on getting versions.json from the server.");
+            throw;
+        }
+
+        latestLauncherInfo = string.IsNullOrEmpty(jsonData)
+                    ? new LatestLauncherVersion()
+                    : JsonSerializer.Deserialize<LatestLauncherVersion>(jsonData);
+    }
+
+    if (latestLauncherInfo is not null)
+    {
+        string[] version = latestLauncherInfo.latest.Split('.');
+        logger.Information("Latest version on server: {0}", latestLauncherInfo.latest);
+        string[] versionOnServer = fileVersionInfo.FileVersion.Split('.');
+        logger.Information("Launcher version: {0}", fileVersionInfo.FileVersion);
+
+        if (Int16.Parse(version[0]) > Int16.Parse(versionOnServer[0]) ||
+            Int16.Parse(version[1]) > Int16.Parse(versionOnServer[1]) ||
+            Int16.Parse(version[2]) > Int16.Parse(versionOnServer[2]) ||
+            Int16.Parse(version[3]) > Int16.Parse(versionOnServer[3]))
+        {
+            WebClient webClient = new WebClient();
+
+            try
+            {
+                logger.Information("Downloading latest.zip");
+                webClient.DownloadFile(new System.Uri("https://files.veloe.link/launcher/update/latest.zip"), "latest.zip");
+            }
+            catch (Exception ex)
+            {
+                logger.Error("Error occured on getting latest.zip from the server.");
+                throw;
+            }
+
+            logger.Information("Unpacking latest.zip");
+            ZipFile.ExtractToDirectory($"latest.zip", Directory.GetCurrentDirectory(), true);
+
+            logger.Information("Deleting latest.zip");
+            File.Delete($"latest.zip");
+        }
+        else
+        {
+            logger.Information("No update required.");
+        }
+
+    }
+}
+catch (Exception ex)
+{
+    logger.Error(ex.Message);
+    logger.Error("Path exe: {0}", Directory.GetCurrentDirectory());
+    logger.Error(ex.StackTrace);
+}
+
+

+ 16 - 0
VeloeLauncherUpdater/VeloeLauncherUpdater.csproj

@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+	  <OutputType>WinExe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <DebugType>embedded</DebugType>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Serilog" Version="2.11.0" />
+    <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
+  </ItemGroup>
+
+</Project>

+ 31 - 0
VeloeMinecraftLauncher.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32228.430
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VeloeMinecraftLauncher", "VeloeMinecraftLauncher\VeloeMinecraftLauncher.csproj", "{AD6EAFD6-B147-4F3D-9942-9ECA2FC139DC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VeloeLauncherUpdater", "VeloeLauncherUpdater\VeloeLauncherUpdater.csproj", "{870555F2-281D-45E6-B1C1-FB063C4B9E59}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{AD6EAFD6-B147-4F3D-9942-9ECA2FC139DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AD6EAFD6-B147-4F3D-9942-9ECA2FC139DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AD6EAFD6-B147-4F3D-9942-9ECA2FC139DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AD6EAFD6-B147-4F3D-9942-9ECA2FC139DC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{870555F2-281D-45E6-B1C1-FB063C4B9E59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{870555F2-281D-45E6-B1C1-FB063C4B9E59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{870555F2-281D-45E6-B1C1-FB063C4B9E59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{870555F2-281D-45E6-B1C1-FB063C4B9E59}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {94B35E4F-CB75-4AE7-81CD-422587C8CA3D}
+	EndGlobalSection
+EndGlobal

+ 454 - 0
VeloeMinecraftLauncher/.gitignore

@@ -0,0 +1,454 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# Tye
+.tye/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+##
+## Visual studio for Mac
+##
+
+
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+##
+## Visual Studio Code
+##
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json

+ 12 - 0
VeloeMinecraftLauncher/App.axaml

@@ -0,0 +1,12 @@
+<Application xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:local="using:VeloeMinecraftLauncher"
+             x:Class="VeloeMinecraftLauncher.App">
+    <Application.DataTemplates>
+        <local:ViewLocator/>
+    </Application.DataTemplates>
+
+    <Application.Styles>
+        <FluentTheme Mode="Dark"/>
+    </Application.Styles>
+</Application>

+ 29 - 0
VeloeMinecraftLauncher/App.axaml.cs

@@ -0,0 +1,29 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using VeloeMinecraftLauncher.ViewModels;
+using VeloeMinecraftLauncher.Views;
+
+namespace VeloeMinecraftLauncher
+{
+    public partial class App : Application
+    {
+        public override void Initialize()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+
+        public override void OnFrameworkInitializationCompleted()
+        {
+            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                desktop.MainWindow = new MainWindow
+                {
+                    DataContext = new MainWindowViewModel(),
+                };
+            }
+
+            base.OnFrameworkInitializationCompleted();
+        }
+    }
+}

BIN
VeloeMinecraftLauncher/Assets/avalonia-logo.ico


+ 42 - 0
VeloeMinecraftLauncher/MinecraftLauncher/Downloader.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.MinecraftLauncher
+{
+    internal static class Downloader
+    {
+        public static T DownloadAndDeserializeJsonData<T>(string url) where T : new()
+        {
+            using (var webClient = new HttpClient())
+            {
+                var jsonData = string.Empty;
+                try
+                {
+                    jsonData = webClient.GetStringAsync(url).Result;
+                }
+                catch (Exception) { }
+
+                return string.IsNullOrEmpty(jsonData)
+                            ? new T()
+                            : JsonSerializer.Deserialize<T>(jsonData);
+            }
+        }
+
+        public static void DownloadFile(string url, string path, string filename)
+        {
+            using (var webClient = new WebClient())
+            {
+                if (!Directory.Exists(path))
+                    Directory.CreateDirectory(path);
+                webClient.DownloadFileAsync(new System.Uri(url), path + "\\" + filename);
+            }
+        }
+    }
+}

+ 46 - 0
VeloeMinecraftLauncher/MinecraftLauncher/EventSink.cs

@@ -0,0 +1,46 @@
+using Serilog.Events;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.MinecraftLauncher    
+{
+    public class EventSink : Serilog.Core.ILogEventSink
+    {
+        private readonly IFormatProvider _formatProvider = null;
+        public event EventHandler DataReceived;
+
+        public EventSink(IFormatProvider formatProvider)
+        {
+            _formatProvider = formatProvider;
+        }
+
+        public void Emit(LogEvent logEvent)
+        {
+            var message = "";
+            if (_formatProvider is not null)
+                message = logEvent.RenderMessage(_formatProvider);
+            else
+            {
+                message = $"[{logEvent.Timestamp.ToString("HH:mm:ss")} {logEvent.Level.ToString()}] {logEvent.RenderMessage()}{logEvent.Exception}\n";
+            }
+            if (DataReceived != null)
+            {
+                var data = EventArgs.Empty;
+                DataReceived?.Invoke(this, new MyEventArgs(message));
+            }
+        }
+    }
+
+    public class MyEventArgs : EventArgs
+    {
+        public string Data;
+        public MyEventArgs(string _myData)
+        {
+            Data = _myData;
+        }
+    }
+}

+ 115 - 0
VeloeMinecraftLauncher/MinecraftLauncher/Settings.cs

@@ -0,0 +1,115 @@
+using Microsoft.Extensions.Logging;
+using Serilog;
+using Serilog.Events;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.MinecraftLauncher
+{
+    internal static class Settings
+    {
+        //public static readonly string JavaPath = "C:\\Program Files\\Microsoft\\jdk-11.0.12.7-hotspot\\bin\\java.exe";
+        public static string JavaPath = "";
+        public static string MinecraftForlderPath = "";
+        public static UInt32 MaxRam = 2048;
+        public static bool useCustomJava = false;
+        public static bool setMaxRam = false;
+        public static bool setPath = true;
+        public static bool checkGameAssets = false;
+        public static bool gameLogToLauncher = false;
+        public static string Username;
+        public static string lastChosenVersion;
+        public static Serilog.ILogger logger;
+
+        public static void UpdateSettings(SettingsSerializable settings)
+        {
+            if (settings is null)
+                return;
+            JavaPath = settings.JavaPath;
+            MinecraftForlderPath = settings.MinecraftForlderPath;
+            MaxRam = settings.MaxRam;
+            useCustomJava = settings.useCustomJava;
+            setMaxRam = settings.setMaxRam;
+            setPath = settings.setPath;
+            checkGameAssets = settings.checkGameAssets;
+            gameLogToLauncher = settings.gameLogToLauncher;
+            lastChosenVersion = settings.lastChosenVersion;
+            Username = settings.Username;
+        }
+
+        public static void LoadSettings()
+        {
+            if (!File.Exists($"settings.json"))
+            {
+                var file = File.Create($"settings.json");
+                file.Close();
+                file.Dispose();
+            }
+
+            var settings = File.ReadAllText($"settings.json");
+
+            if (settings != String.Empty)
+            {
+                try
+                {
+                    var settingsSerializable = JsonSerializer.Deserialize<SettingsSerializable>(settings);
+
+                    Settings.UpdateSettings(settingsSerializable);
+                }
+                catch (JsonException ex)
+                {
+                    //TODO log
+                }
+            }
+        }
+
+        public static void SaveSettings()
+        {
+            if(MinecraftForlderPath.Last() is not ('/' or '\\'))
+                MinecraftForlderPath = MinecraftForlderPath + "/";
+
+            if (!Directory.Exists(MinecraftForlderPath))
+                Directory.CreateDirectory(MinecraftForlderPath);
+
+            var settings = JsonSerializer.Serialize(new SettingsSerializable());
+
+            File.WriteAllText($"settings.json", settings);
+        }
+    }
+
+    public class SettingsSerializable
+    {
+        public string JavaPath {get; set;}
+        public string MinecraftForlderPath { get; set;}
+        public UInt32 MaxRam { get; set; }
+        public bool useCustomJava { get; set; }
+        public bool setMaxRam { get; set; }
+        public bool setPath { get; set; }
+        public bool checkGameAssets { get; set; }
+
+        public bool gameLogToLauncher { get; set; }
+
+        public string Username { get; set; }
+
+        public string lastChosenVersion { get; set; }
+
+        public SettingsSerializable()
+        {
+            JavaPath = Settings.JavaPath;
+            MinecraftForlderPath= Settings.MinecraftForlderPath;
+            MaxRam= Settings.MaxRam;
+            useCustomJava= Settings.useCustomJava;
+            setMaxRam= Settings.setMaxRam;
+            setPath = Settings.setPath;
+            checkGameAssets= Settings.checkGameAssets;
+            gameLogToLauncher= Settings.gameLogToLauncher;
+            Username = Settings.Username;
+            lastChosenVersion= Settings.lastChosenVersion;
+        }
+    }
+}

+ 156 - 0
VeloeMinecraftLauncher/MinecraftLauncher/StartCommandBuilder.cs

@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.MinecraftLauncher
+{
+    internal static class StartCommandBuilder
+    {
+        public static string Build(Entity.Version.Version version, string username)
+        {
+            var returnString = new StringBuilder();
+            if (version is null)
+                return returnString.ToString();
+
+            if(Settings.setMaxRam)
+                returnString.Append($"-Xmx{Settings.MaxRam}M ");
+
+            //for forge
+            if (version.inheritsFrom is null)
+                returnString.Append($"-Djava.library.path={Settings.MinecraftForlderPath + "versions\\" + version.id + "\\natives\\"}");
+            else
+                returnString.Append($"-Djava.library.path={Settings.MinecraftForlderPath + "versions\\" + version.inheritsFrom + "\\natives\\"}");
+
+            returnString.Append(" -cp ");
+
+            foreach(var library in version.libraries)
+            {
+                if (library.natives is null)
+                    returnString.Append(Settings.MinecraftForlderPath + "libraries\\" + library.downloads.artifact.path.Replace("/", "\\") + ";");
+
+            }
+            //for forge
+            Entity.Version.Version inheritsFrom = new();
+            if (version.inheritsFrom is null)
+                returnString.Append(Settings.MinecraftForlderPath + "versions/" + version.id + "/" + version.id + ".jar");
+            else
+            {
+                var inheritsJsonString = File.ReadAllText(Settings.MinecraftForlderPath + "versions/" + version.inheritsFrom + "/" + version.inheritsFrom + ".json");
+                
+                inheritsFrom = JsonSerializer.Deserialize<Entity.Version.Version>(inheritsJsonString);
+
+                foreach (var library in inheritsFrom.libraries)
+                {
+                    if (library.natives is null)
+                        returnString.Append(Settings.MinecraftForlderPath + "libraries\\" + library.downloads.artifact.path.Replace("/", "\\") + ";");
+    
+                }
+                returnString.Append(Settings.MinecraftForlderPath + "versions/" + version.inheritsFrom + "/" + version.inheritsFrom + ".jar");
+            }
+
+            returnString.Append(" " + version.mainClass);
+
+            List<string> args = new List<string>();
+            List<string> argsValues = new List<string>();
+
+            if (version.arguments is not null)
+                foreach (var argument in version.arguments.game)
+                {
+                    if (argument is null)
+                        continue;
+
+                    var type = argument.GetType();
+
+                    if (!(argument is JsonElement))
+                        continue;
+
+                    if (!(((JsonElement)argument).ValueKind == JsonValueKind.String))
+                        continue;
+
+                    var value = ((JsonElement)argument).Deserialize(typeof(string));
+
+                    //if ()
+
+                    if (!(value as string).Contains("--"))
+                    {
+                        argsValues.Add(value as string);
+                        continue;
+                    }
+
+                    args.Add(value as string);
+                }
+
+            if (version.minecraftArguments is not null)
+            {
+                var minecraftArguments = version.minecraftArguments.Split(' ');
+                //args = version.minecraftArguments.Split(' ').Where(x=> x.Contains("--")).ToList();
+                for (int i = 0; i < minecraftArguments.Count(); i++)
+                {
+                    if (minecraftArguments[i].Contains("--"))
+                        args.Add(minecraftArguments[i]);
+                    else
+                        argsValues.Add(minecraftArguments[i]);
+                }
+            }
+
+            for (int i = 0; i < args.Count; i++)
+            {
+                switch (args[i])
+                {
+                    case "--username":
+                        returnString.Append(" --username " + username);
+                        break;
+                    case "--version":
+                        returnString.Append(" --version " + "\"Copy of VeloeLauncher\"");
+                        break;
+                    case "--gameDir":
+                        //for forge
+                        if (!(argsValues.Where(x => x.Contains("forge")).Count() > 0 || argsValues.Where(x => x.Contains("fabric")).Count() > 0))
+                            returnString.Append(" --gameDir " + Settings.MinecraftForlderPath);
+                        else
+                            returnString.Append(" --gameDir " + Settings.MinecraftForlderPath + "versions\\" + version.id);
+                        break;
+                    case "--assetsDir":
+                        //for forge
+                        if (version.inheritsFrom is null)
+                            returnString.Append(" --assetsDir " + Settings.MinecraftForlderPath + "assets\\" + version.assets + "\\");
+                        else
+                            returnString.Append(" --assetsDir " + Settings.MinecraftForlderPath + "assets\\" + inheritsFrom.assets + "\\");
+                        break;
+                    case "--assetIndex":
+                        //for forge
+                        if (version.inheritsFrom is null)
+                            returnString.Append(" --assetIndex " + version.assets);
+                        else
+                            returnString.Append(" --assetIndex " + inheritsFrom.assets);
+                        break;
+                    case "--uuid":
+                        returnString.Append(" --uuid sample_token");
+                        break;
+                    case "--accessToken":
+                        returnString.Append(" --accessToken sample_token");
+                        break;
+                    case "--userType":
+                        returnString.Append(" --userType offline");
+                        break;
+                    case "--userProperties":
+                        returnString.Append(" --userProperties  " + "{\"veloelauncher\":[\"wtfisthis\"]}");
+                        break;
+                    case "--versionType":
+                        returnString.Append(" --versionType " + version.type);
+                        break;
+                    case "--tweakClass":
+                        returnString.Append(" --tweakClass");
+                        if (argsValues[i] is not null)
+                            returnString.Append(" " + argsValues[i]);
+                        break;
+                }
+            }
+            return returnString.ToString();
+        }
+    }
+}

+ 24 - 0
VeloeMinecraftLauncher/Models/Entity/Assets/Asset.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.Assets
+{
+    internal class Asset
+    {
+        public string hash { get; set; }
+        public int size { get; set; }
+    }
+
+    internal class AssetsManifest
+    {
+        [JsonPropertyName("virtual")]
+        public bool IsVirtual { get; set; }
+
+        [JsonPropertyName("objects")]
+        public Dictionary<string, Asset> Objects { get; set; }
+    }
+}

+ 29 - 0
VeloeMinecraftLauncher/Models/Entity/LauncherProfiles.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.LauncherProfiles
+{
+    [Serializable]
+    public class LauncherProfiles
+    {
+        public string selectedProfile { get; set; }
+        public Dictionary<string, Profile> profiles { get; set; }
+
+        public LauncherProfiles()
+        {
+            profiles = new Dictionary<string, Profile>();
+        }
+    }
+
+    [Serializable]
+    public class Profile
+    {
+        public string name { get; set; }
+        public string lastVersionId { get; set; }
+        public string[] allowedReleaseTypes { get; set; }
+        public string launcherVisibilityOnGameClose { get; set; }
+    }
+}

+ 23 - 0
VeloeMinecraftLauncher/Models/Entity/McStatus/McStatus.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.McStatus
+{
+    public class McStatus
+    {
+        public string MessageOfTheDay { get; set; }
+        public string Gametype { get; set; }
+        public string GameId { get; set; }
+        public string Version { get; set; }
+        public string Plugins { get; set; }
+        public string Map { get; set; }
+        public string NumPlayers { get; set; }
+        public string MaxPlayers { get; set; }
+        public string HostPort { get; set; }
+        public string HostIp { get; set; }
+        public List<string> Players { get; set; }
+    }
+}

+ 191 - 0
VeloeMinecraftLauncher/Models/Entity/Version/Version.cs

@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.Version
+{
+
+    public class Arguments
+    {      
+        public List<object> game { get; set; }
+        public List<object> jvm { get; set; }
+    }
+    public class Artifact
+    {
+        public string path { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class AssetIndex
+    {
+        public string id { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public int totalSize { get; set; }
+        public string url { get; set; }
+    }
+
+    public class Classifiers
+    {
+        [JsonPropertyName("natives-linux")]
+        public NativesLinux NativesLinux { get; set; }
+
+        [JsonPropertyName("natives-osx")]
+        public NativesOsx NativesOsx { get; set; }
+
+        [JsonPropertyName("natives-windows")]
+        public NativesWindows NativesWindows { get; set; }
+
+        [JsonPropertyName("natives-windows-32")]
+        public NativesWindows NativesWindows32 { get; set; }
+
+        [JsonPropertyName("natives-windows-64")]
+        public NativesWindows NativesWindows64 { get; set; }
+
+        public Sources sources { get; set; }
+        public Javadoc javadoc { get; set; }
+    }
+
+    public class Client
+    {
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+        public string argument { get; set; }
+        public File file { get; set; }
+        public string type { get; set; }
+    }
+
+    public class Downloads
+    {
+        public Client client { get; set; }
+        public Server server { get; set; }
+        public Artifact artifact { get; set; }
+        public Classifiers classifiers { get; set; }
+    }
+
+    public class Extract
+    {
+        public List<string> exclude { get; set; }
+    }
+
+    public class File
+    {
+        public string id { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class Javadoc
+    {
+        public string path { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class JavaVersion
+    {
+        public string component { get; set; }
+        public int majorVersion { get; set; }
+    }
+
+    public class Library
+    {
+        public Downloads downloads { get; set; }
+        public string name { get; set; }
+        public List<Rule> rules { get; set; }
+        public Extract extract { get; set; }
+        public Natives natives { get; set; }
+    }
+
+    public class Logging
+    {
+        public Client client { get; set; }
+    }
+
+    public class Natives
+    {
+        public string linux { get; set; }
+        public string osx { get; set; }
+        public string windows { get; set; }
+    }
+
+    public class NativesLinux
+    {
+        public string path { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class NativesOsx
+    {
+        public string path { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class NativesWindows
+    {
+        public string path { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class Os
+    {
+        public string name { get; set; }
+    }
+
+    public class Version
+    {
+        public Arguments arguments { get; set; }
+        public AssetIndex assetIndex { get; set; }
+        public string assets { get; set; }
+        public int complianceLevel { get; set; }
+        public Downloads downloads { get; set; }
+        public string id { get; set; }
+        public JavaVersion javaVersion { get; set; }
+        public List<Library> libraries { get; set; }
+        public Logging logging { get; set; }
+        public string mainClass { get; set; }
+        public string minecraftArguments { get; set; }
+        public int minimumLauncherVersion { get; set; }
+        public DateTime releaseTime { get; set; }
+        public DateTime time { get; set; }
+        public string type { get; set; }
+
+        public string inheritsFrom { get; set; }
+    }
+
+    public class Rule
+    {
+        public string action { get; set; }
+        public Os os { get; set; }
+    }
+
+    public class Server
+    {
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+    public class Sources
+    {
+        public string path { get; set; }
+        public string sha1 { get; set; }
+        public int size { get; set; }
+        public string url { get; set; }
+    }
+
+}

+ 14 - 0
VeloeMinecraftLauncher/Models/Entity/VesrionManifest/Latest.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.VersionManifest
+{
+    public class Latest
+    {
+        public string release { get; set; }
+        public string snapshot { get; set; }
+    }
+}

+ 24 - 0
VeloeMinecraftLauncher/Models/Entity/VesrionManifest/Version.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.VersionManifest
+{ 
+    public class Version
+    { 
+        public string id { get; set; }
+        public string type { get; set; }
+        public string url { get; set; }
+        public DateTime time { get; set; }
+        public DateTime releaseTime { get; set; }
+        public string sha1 { get; set; }
+        public int complianceLevel { get; set; }
+
+        public override string ToString()
+        {
+            return id;
+        }
+    }
+}

+ 14 - 0
VeloeMinecraftLauncher/Models/Entity/VesrionManifest/VersionManifest.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.Entity.VersionManifest
+{
+    public class VersionManifest
+    {
+        public Latest latest { get; set; }
+        public List<Version> versions { get; set; }
+    }
+}

+ 24 - 0
VeloeMinecraftLauncher/Program.cs

@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.ReactiveUI;
+using System;
+
+namespace VeloeMinecraftLauncher
+{
+    internal class Program
+    {
+        // Initialization code. Don't use any Avalonia, third-party APIs or any
+        // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+        // yet and stuff might break.
+        [STAThread]
+        public static void Main(string[] args) => BuildAvaloniaApp()
+            .StartWithClassicDesktopLifetime(args);
+
+        // Avalonia configuration, don't remove; also used by visual designer.
+        public static AppBuilder BuildAvaloniaApp()
+            => AppBuilder.Configure<App>()
+                .UsePlatformDetect()
+                .LogToTrace()
+                .UseReactiveUI();
+    }
+}

+ 35 - 0
VeloeMinecraftLauncher/VeloeMinecraftLauncher.csproj

@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <!--Avalonia doesen't support TrimMode=link currently,but we are working on that https://github.com/AvaloniaUI/Avalonia/issues/6892 -->
+    <TrimMode>copyused</TrimMode>
+    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
+    <DebugType>embedded</DebugType>
+  </PropertyGroup>
+  <ItemGroup>
+    <Folder Include="Models\" />
+    <AvaloniaResource Include="Assets\**" />
+    <None Remove=".gitignore" />
+  </ItemGroup>
+  <ItemGroup>
+    <!--This helps with theme dll-s trimming.
+	If you will publish your application in self-contained mode with p:PublishTrimmed=true and it will use Fluent theme Default theme will be trimmed from the output and vice versa.
+	https://github.com/AvaloniaUI/Avalonia/issues/5593 -->
+    <TrimmableAssembly Include="Avalonia.Themes.Fluent" />
+    <TrimmableAssembly Include="Avalonia.Themes.Default" />
+  </ItemGroup>
+  <ItemGroup>
+    <PackageReference Include="Avalonia" Version="0.10.18" />
+    <PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
+    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
+    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
+    <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.18" />
+    <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.8" />
+    <PackageReference Include="ReactiveUI.Validation" Version="3.0.1" />
+    <PackageReference Include="Serilog" Version="2.11.0" />
+    <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
+    <PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
+  </ItemGroup>
+</Project>

+ 30 - 0
VeloeMinecraftLauncher/ViewLocator.cs

@@ -0,0 +1,30 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using System;
+using VeloeMinecraftLauncher.ViewModels;
+
+namespace VeloeMinecraftLauncher
+{
+    public class ViewLocator : IDataTemplate
+    {
+        public IControl Build(object data)
+        {
+            var name = data.GetType().FullName!.Replace("ViewModel", "View");
+            var type = Type.GetType(name);
+
+            if (type != null)
+            {
+                return (Control)Activator.CreateInstance(type)!;
+            }
+            else
+            {
+                return new TextBlock { Text = "Not Found: " + name };
+            }
+        }
+
+        public bool Match(object data)
+        {
+            return data is ViewModelBase;
+        }
+    }
+}

+ 622 - 0
VeloeMinecraftLauncher/ViewModels/MainWindowViewModel.cs

@@ -0,0 +1,622 @@
+using Avalonia.Controls.ApplicationLifetimes;
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using VeloeMinecraftLauncher.Entity.LauncherProfiles;
+using VeloeMinecraftLauncher.Entity.VersionManifest;
+using VeloeMinecraftLauncher.MinecraftLauncher;
+using VeloeMinecraftLauncher.Views;
+using System;
+using System.Threading.Tasks;
+using System.Windows;
+using Microsoft.AspNetCore.SignalR.Client;
+using VeloeMinecraftLauncher.Entity.McStatus;
+using System.Timers;
+using System.ComponentModel;
+using System.Reflection;
+using Serilog;
+using Serilog.Events;
+using Serilog.Formatting;
+using Avalonia.Controls;
+using Avalonia.Threading;
+
+namespace VeloeMinecraftLauncher.ViewModels
+{
+    public class MainWindowViewModel : ViewModelBase
+    {
+        public MainWindowViewModel()
+        {
+            //creating logger
+            EventSink eventSink = new(null);
+            eventSink.DataReceived += LogHandler;
+
+            logger = new LoggerConfiguration()
+               .MinimumLevel.Debug()
+               .WriteTo.Sink(eventSink)
+               .WriteTo.File("launcher.log", LogEventLevel.Debug, fileSizeLimitBytes: 100000000)// restricted... is Optional
+               .CreateLogger();
+            Settings.logger = logger;
+
+            //loading settings
+            logger.Debug("Loading settings.");
+            Settings.LoadSettings();
+            username = Settings.Username;
+
+            //loading local verions
+            logger.Debug("Loading local versions.");
+            updateAvailable();
+
+            //check launcher update
+            logger.Debug("Checking launcher versions updates.");
+            latestLauncherInfo = Downloader.DownloadAndDeserializeJsonData<LatestLauncherVersion>("https://files.veloe.link/launcher/update/versions.json");
+            if (latestLauncherInfo is not null)
+            {
+                logger.Debug("Launcher version on server: {0}", latestLauncherInfo.latest);
+                logger.Debug("Launcher version: {0}", Assembly.GetExecutingAssembly().GetName().Version);
+                string[] version = latestLauncherInfo.latest.Split('.');
+
+                var vat = Assembly.GetExecutingAssembly().GetName().Version;
+
+                if (Int16.Parse(version[0]) > Assembly.GetExecutingAssembly().GetName().Version.Major ||
+                    Int16.Parse(version[1]) > Assembly.GetExecutingAssembly().GetName().Version.Minor ||
+                    Int16.Parse(version[2]) > Assembly.GetExecutingAssembly().GetName().Version.Build ||
+                    Int16.Parse(version[3]) > Assembly.GetExecutingAssembly().GetName().Version.Revision)
+                {
+                    logger.Debug("Update available!");
+                    IsUpdateAvailable = true;
+                }
+            }
+
+            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(() => { 
+                logger.Warning("Reconnecting to WebCoket...");
+            });
+            Func<string, Task> reconnected = str => Task.Run(() => { 
+                logger.Warning("Reconnected to WebCoket.");
+                connection.InvokeAsync("ConnectToGroup", "McTFC");
+                connection.InvokeAsync("ConnectToGroup", "McVanilla");
+                connection.InvokeAsync("ConnectToGroup", "McTech");
+            });
+
+            connection.Reconnecting += reconnecting;
+            connection.Reconnected += reconnected;
+
+            connection.On<string>("UpdateMcTFC", (message) =>
+            {
+                McStatus status = JsonSerializer.Deserialize<McStatus>(message);
+                McTfcBlock = $"McTFC: Online {status.NumPlayers}/{status.MaxPlayers}";
+                mcTfcTimer.Stop();
+                mcTfcTimer.Start();
+                //ConsoleText += message;
+            });
+
+            connection.On<string>("UpdateMcVanilla", (message) =>
+            {
+                McStatus status = JsonSerializer.Deserialize<McStatus>(message);
+                McTfcBlock = $"McVanilla: Online {status.NumPlayers}/{status.MaxPlayers}";
+                mcTechTimer.Stop();
+                mcTechTimer.Start();
+                //ConsoleText += message;
+            });
+
+            connection.On<string>("UpdateMcTech", (message) =>
+            {
+                McStatus status = JsonSerializer.Deserialize<McStatus>(message);
+                McTfcBlock = $"McTech: Online {status.NumPlayers}/{status.MaxPlayers}";
+                mcVanillaTimer.Stop();
+                mcVanillaTimer.Start();
+                //ConsoleText += message;
+            });
+
+            mcTfcTimer.Elapsed += OnTimedEventMcTfc;
+            mcTfcTimer.Start();
+            mcTechTimer.Elapsed += OnTimedEventMcTech;
+            mcTechTimer.Start();
+            mcVanillaTimer.Elapsed += OnTimedEventMcVanilla;
+            mcVanillaTimer.Start();
+
+            connection.StartAsync();
+
+            connection.InvokeAsync("ConnectToGroup", "McTFC");
+            connection.InvokeAsync("ConnectToGroup", "McVanilla");
+            connection.InvokeAsync("ConnectToGroup", "McTech");
+
+            consoleOutputTimer.Elapsed += UpdateConsoleOutput;
+            consoleOutputTimer.AutoReset = false;
+        }
+
+        int i = 0;
+
+        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;
+        private string downloadButton = "Download versions";
+        private string startButton = "Start Minecraft";
+        private string username;
+        private StringBuilder consoleText = new StringBuilder();
+        private int downloadedIndex;
+        private string argumentsBox;
+        private bool isNoGameRunning = true;
+        private bool isUpdateAvailable = false;
+        private string settingsButton = "Settings";
+        private string mcTfcBlock = "Wait for update...";
+        private string mcTechBlock = "Wait for update...";
+        private string mcVanillaBlock = "Wait for update...";
+
+        ILogger logger;
+
+        LatestLauncherVersion latestLauncherInfo;
+        DownloadedVerion downloadedVersion;
+
+        ObservableCollection<DownloadedVerion> downloadedVersions;
+
+        public ObservableCollection<DownloadedVerion> DownloadedVersions { 
+            get => downloadedVersions; 
+            set
+            {
+                this.RaiseAndSetIfChanged(ref downloadedVersions, value);
+            }
+        }
+
+        public DownloadedVerion DownloadedVerion
+        {
+            get => downloadedVersion;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref downloadedVersion, value);
+                //Settings.lastChosenVersion = value.version;
+                //logger.Debug("Version choosen: {0}",value.version);
+                //logger.Debug("Version json: {0}", value.path);
+            }
+        }
+
+        public int DownloadedIndex
+        {
+            get => downloadedIndex;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref downloadedIndex, value);
+                if (value >= 0 && value < DownloadedVersions.Count)
+                    DownloadedVerion = DownloadedVersions[value];
+            }
+        }
+        public string Greeting => "Welcome to Cringe Launcher!";
+        public string DownloadButton { 
+            get => downloadButton; 
+            set 
+            {
+               this.RaiseAndSetIfChanged(ref downloadButton, value);
+            } 
+        }
+
+        public string StartButton
+        {
+            get => startButton;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref startButton, value);
+            }
+        }
+
+        public string Username 
+        { 
+            get => username;
+            set => this.RaiseAndSetIfChanged(ref username, value);
+        }
+
+        public string ConsoleText
+        {
+            get 
+            {
+                if (consoleText.Length < UInt16.MaxValue)
+                    return consoleText.ToString();
+                else
+                    return consoleText.ToString(consoleText.Length - UInt16.MaxValue, UInt16.MaxValue);
+            }
+            set
+            {
+                consoleText.Clear();
+                consoleText.Append(value);
+                this.RaisePropertyChanged(nameof(ConsoleText));
+                ConsoleTextCaretIndex = int.MaxValue;
+            }
+        }
+
+        public string ArgumentsBox
+        {
+            get => argumentsBox;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref argumentsBox, value);
+            }
+        }
+
+        public string SettingsButton
+        {
+            get => settingsButton;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref settingsButton, value);
+            }
+        }
+
+        public bool IsNoGameRunning
+        {
+            get => isNoGameRunning;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref isNoGameRunning, value);
+            }
+        }
+
+        public bool IsUpdateAvailable
+        {
+            get => isUpdateAvailable;
+            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 int ConsoleTextCaretIndex
+        {
+            get { return int.MaxValue; }
+            set
+            {
+                this.RaisePropertyChanged(nameof(ConsoleTextCaretIndex));
+            }
+        }
+
+
+
+        public void OnClickCommand()
+        {
+            var versionsDownloader = new VersionsDownloader { DataContext = new VersionsDownloaderViewModel() };
+           
+            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                //var versionsDownloaderTask =
+                versionsDownloader.Closed += updateAvailable;
+                versionsDownloader.ShowDialog(desktop.MainWindow);
+                //versionsDownloaderTask.Wait();
+                
+            }
+
+        }
+
+        public void StartMinecraft()
+        {
+            logger.Debug("Starting minecraft.");
+            if (DownloadedVerion is null)
+                return;
+
+            int version = 0;
+
+            //var verionJsonFileStream = File.OpenRead(DownloadedVerion.path);
+            using (StreamReader reader = new StreamReader(DownloadedVerion.path))
+            {
+                string json = reader.ReadToEnd();
+
+                var versionJson = JsonSerializer.Deserialize<Entity.Version.Version>(json);
+
+                string arguments = StartCommandBuilder.Build(versionJson, Username);
+
+                if (!Settings.useCustomJava)
+                {
+                    if (versionJson.javaVersion != null)
+                    {
+                        logger.Debug("Java version required: {0}", versionJson.javaVersion.majorVersion);
+                        version = versionJson.javaVersion.majorVersion;
+                    }
+                    else
+                    {
+                        if (versionJson.inheritsFrom != null)
+                        {
+                            using (StreamReader inheritsFromReader = new StreamReader(Settings.MinecraftForlderPath + "versions/" + versionJson.inheritsFrom + "/" + versionJson.inheritsFrom + ".json"))
+                            {
+                                string jsonInheritsFrom = inheritsFromReader.ReadToEnd();
+                                var inheritsFromJson = JsonSerializer.Deserialize<Entity.Version.Version>(jsonInheritsFrom);
+                                if (inheritsFromJson.javaVersion != null)
+                                {
+                                    logger.Debug("Java version required: {0}", inheritsFromJson.javaVersion.majorVersion);
+                                    version = inheritsFromJson.javaVersion.majorVersion;
+                                }
+                            }
+                        }
+                    }
+                }
+            
+            ConsoleText += arguments;
+            ArgumentsBox = arguments;
+            }
+
+            if (DownloadedVerion is null)
+                return;
+
+            string javaPath = Settings.JavaPath;
+
+            if (!Settings.useCustomJava)
+                javaPath = Settings.MinecraftForlderPath + "javaruntime/" + version + "/bin/java.exe";
+
+            logger.Debug("Java version path: {0}", javaPath);
+            logger.Debug("Minecraft arguments: {0}", ArgumentsBox);
+
+            ProcessStartInfo proc;
+            proc = new ProcessStartInfo
+            {
+                UseShellExecute = false,
+                RedirectStandardOutput = true,
+                RedirectStandardError = true,
+                WindowStyle = ProcessWindowStyle.Hidden,
+                CreateNoWindow = true,
+                FileName = System.IO.Path.Combine(Settings.MinecraftForlderPath, javaPath),
+                StandardErrorEncoding = Encoding.UTF8,
+                WorkingDirectory = Settings.MinecraftForlderPath,
+                Arguments = ArgumentsBox
+            };
+
+
+            Process minecraft = new Process();
+
+            minecraft.StartInfo = proc;
+
+            Task.Run(() =>
+            {
+                logger.Debug("Starting java process.");
+                
+                minecraft.OutputDataReceived += OutputHandler;                                
+                minecraft.ErrorDataReceived += OutputHandler;
+
+                //minecraft.Exited += ProcessExited; //dont work properly
+                //* Start process and handlers            
+                //minecraft.WaitForExit();
+                minecraft.Start();
+
+                minecraft.BeginOutputReadLine();                               
+                minecraft.BeginErrorReadLine();
+
+                if (!Settings.gameLogToLauncher)
+                {
+                    minecraft.OutputDataReceived -= OutputHandler;
+                    minecraft.CancelOutputRead();
+                }
+
+                return Task.CompletedTask;
+            });
+            logger.Debug("Updating Username in Settings");
+            Settings.Username = username;
+            Settings.lastChosenVersion = DownloadedVerion.version;
+            Settings.SaveSettings();
+
+        }
+
+        void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
+        {
+            //Todo create multiple TextBlocks if char limit
+            if (!consoleOutputTimer.Enabled)
+            {
+                consoleOutputTimer.Stop();
+                consoleOutputTimer.Start();
+            }
+            consoleText.Append(outLine.Data + "\n");          
+        }
+
+        void LogHandler(object sendingProcess, EventArgs args)
+        {
+            if (!consoleOutputTimer.Enabled)
+            {
+                consoleOutputTimer.Stop();
+                consoleOutputTimer.Start();
+            }
+            consoleText.Append(((MyEventArgs)args).Data);
+        }
+
+        void UpdateConsoleOutput(Object source, ElapsedEventArgs e)
+        {
+            this.RaisePropertyChanged(nameof(ConsoleText));
+
+            ScrollToEnd("ConsoleScroll");
+        }
+
+        void ScrollToEnd(string ScrollName)
+        {
+            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    var scroll = desktop.MainWindow.GetControl<ScrollViewer>("ConsoleScroll");
+                    scroll.ScrollToEnd();
+                });
+
+            }
+        }
+
+        //dont work properly
+        void ProcessExited(object sendingProcess, EventArgs e)
+        {
+            IsNoGameRunning = true;
+            ((Process)sendingProcess).Dispose();           
+        }
+
+
+
+        public void updateAvailable()
+        {
+            if (DownloadedVersions is null)
+                DownloadedVersions = new();
+
+            DownloadedVersions.Clear();
+            DirectoryInfo versions = new( Settings.MinecraftForlderPath + "versions");
+            try
+            {
+                var dirs = versions.GetDirectories("*", SearchOption.TopDirectoryOnly);
+                LauncherProfiles profiles = new LauncherProfiles();
+                profiles.selectedProfile = "NotImplemented";
+
+                foreach (var dir in dirs)
+                {
+                    string checkedPath;
+                    if (File.Exists(checkedPath = dir.FullName + "\\" + dir.Name + ".json"))
+                    {
+                        DownloadedVersions.Add(new DownloadedVerion() { path = checkedPath, version = dir.Name });
+                        profiles.profiles.Add($"Version {dir.Name}", new Profile() { name = dir.Name, lastVersionId = dir.Name, launcherVisibilityOnGameClose = "keep the launcher open" });
+                    }
+
+                }
+
+                if (Settings.lastChosenVersion != null)
+                {
+                    DownloadedIndex = 0;
+                    for (int i = 0; i < DownloadedVersions.Count; i++)
+                    {
+                        if (DownloadedVersions[i].version == Settings.lastChosenVersion)
+                        {
+                            DownloadedIndex = i;
+                            break;
+                        }
+                    }
+                    
+                }
+                if (!File.Exists(Settings.MinecraftForlderPath + "launcher_profiles.json"))
+                {
+                    var file = File.Create(Settings.MinecraftForlderPath + "launcher_profiles.json");
+                    file.Close();
+                    file.Dispose();
+                }
+                var lpString = JsonSerializer.Serialize(profiles);
+                File.WriteAllText(Settings.MinecraftForlderPath + "launcher_profiles.json", lpString);
+            }
+            catch (DirectoryNotFoundException ex)
+            {
+                Directory.CreateDirectory(Settings.MinecraftForlderPath + "versions");
+                return;
+            }
+            
+        }
+
+        public void updateAvailable(object sendingObject, EventArgs e)
+        {
+            updateAvailable();
+
+            switch (sendingObject)
+            {
+                case VersionsDownloader:
+                    ((VersionsDownloader)sendingObject).Closed -= updateAvailable;
+                    break;
+
+                case SettingsWindow:
+                    ((SettingsWindow)sendingObject).Closed -= updateAvailable;
+                    break;
+            }
+    
+        }
+
+        void OpenSettings()
+        {
+            var settingsWindow = new SettingsWindow { DataContext = new SettingsWindowViewModel() };
+
+            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                //var versionsDownloaderTask =
+                settingsWindow.Closed += updateAvailable;
+                settingsWindow.ShowDialog(desktop.MainWindow);
+                //versionsDownloaderTask.Wait();
+                //updateAvailable();
+            }
+        }
+
+        public void DownloadUpdate()
+        {
+            logger.Debug("Started updater.exe");
+            Process updater = new Process();
+            updater.StartInfo.FileName = "updater.exe";
+
+            updater.Start();
+
+            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                //var versionsDownloaderTask =
+                desktop.MainWindow.Close();
+                //versionsDownloaderTask.Wait();
+                //updateAvailable();
+            }
+
+
+        }
+
+        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
+    {
+        public string latest { get; set; }
+        public string url { get; set; }
+    }
+
+    public class DownloadedVerion
+    {
+        public string path;
+        public string version;
+
+        public override string ToString()
+        {
+            return version;
+        }
+    }
+   
+}

+ 170 - 0
VeloeMinecraftLauncher/ViewModels/SettingsWindowViewModel.cs

@@ -0,0 +1,170 @@
+using ReactiveUI;
+using ReactiveUI.Validation.Extensions;
+using System;
+using System.IO;
+using System.Reflection;
+using VeloeMinecraftLauncher.MinecraftLauncher;
+using Avalonia.Controls;
+
+namespace VeloeMinecraftLauncher.ViewModels
+{
+    public class SettingsWindowViewModel : ViewModelBase
+    {
+        public SettingsWindowViewModel()
+        {
+            LauncherVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
+
+            var logger = Settings.logger;
+
+            this.ValidationRule(
+            viewModel => viewModel.MaxRam,
+            value => { UInt32 result; bool resultb = UInt32.TryParse(value, out result); /*logger.Debug("Validator result: {0} {1}", value, resultb);*/ IsValid = resultb; return resultb; },
+            "Not a number.");
+        }
+
+        private string launcherVersion;
+        private string minecraftFolderPath = Settings.MinecraftForlderPath;
+        private string javaPath = Settings.JavaPath;
+        private string maxRam = Settings.MaxRam.ToString();
+        private bool isValid;
+        
+        public bool UseCustomJava 
+            { 
+            get => Settings.useCustomJava; 
+            set 
+            {
+                this.RaiseAndSetIfChanged(ref Settings.useCustomJava, value);
+            } 
+        }
+
+        public bool SetMaxRam
+        {
+            get => Settings.setMaxRam;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref Settings.setMaxRam, value);
+            }
+        }
+
+        public bool SetMinecraftFolder
+        {
+            get => Settings.setPath;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref Settings.setPath, value);
+            }
+        }
+
+        public bool CheckAssets
+        {
+            get => Settings.checkGameAssets;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref Settings.checkGameAssets, value);
+            }
+        }
+
+        public string JavaPath
+        {
+            get => javaPath;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref javaPath, value);
+            }
+        }
+
+        public string MaxRam
+        {
+            get => maxRam.ToString();
+            set
+            {
+                this.RaiseAndSetIfChanged(ref maxRam, value);              
+            }
+        }
+
+        public bool IsValid
+        {
+            get => isValid;
+            set => this.RaiseAndSetIfChanged(ref isValid, value);
+        }
+
+        public string MinecraftFolderPath
+        {
+            get => minecraftFolderPath;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref minecraftFolderPath, value);
+            }
+        }
+
+        public string LauncherVersion
+        {
+            get => launcherVersion;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref launcherVersion, value);
+            }
+        }
+
+        public bool GameLogToLauncher
+        {
+            get => Settings.gameLogToLauncher;
+            set
+            {
+                this.RaiseAndSetIfChanged(ref Settings.gameLogToLauncher, value);
+            }
+        }
+
+        public void SaveSettings()
+        {
+            Settings.JavaPath = javaPath;
+            Settings.MinecraftForlderPath = minecraftFolderPath;
+            Settings.MaxRam = UInt32.Parse(maxRam);
+            Settings.SaveSettings();
+        }
+
+        public void OpenMinecraftPathDialog(Window window)
+        {
+            OpenFolderDialog dialog = new OpenFolderDialog();
+
+            var initPath = String.Empty;
+
+            if (Settings.MinecraftForlderPath is not null)
+                if(Settings.MinecraftForlderPath != String.Empty)
+                   initPath = Path.GetFullPath(Settings.MinecraftForlderPath);
+
+            dialog.InitialDirectory = initPath;
+
+            var result = dialog.ShowAsync(window).Result;
+            if (result is null)
+                return;
+
+            if (result == String.Empty)
+                return;
+
+            MinecraftFolderPath = result;
+        }
+
+        public void OpenJavaPathDialog(Window window)
+        {
+            OpenFolderDialog dialog = new OpenFolderDialog();
+
+            var initPath = String.Empty;
+
+            if (Settings.JavaPath is not null)
+                if (Settings.JavaPath != String.Empty)
+                    initPath = Path.GetFullPath(Settings.JavaPath);
+
+            dialog.InitialDirectory = initPath;
+
+            var result = dialog.ShowAsync(window).Result;
+            if (result is null)
+                return;
+
+            if (result == String.Empty)
+                return;
+
+            JavaPath = result;
+        }
+    }
+    }

+ 695 - 0
VeloeMinecraftLauncher/ViewModels/VersionsDownloaderViewModel.cs

@@ -0,0 +1,695 @@
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using VeloeMinecraftLauncher.Entity.Assets;
+using VeloeMinecraftLauncher.Entity.VersionManifest;
+using VeloeMinecraftLauncher.MinecraftLauncher;
+
+namespace VeloeMinecraftLauncher.ViewModels
+{
+    public class VersionsDownloaderViewModel : ViewModelBase
+    {
+        int i = 0;
+
+        private string startButton = "Download";
+        private bool showOld = false;
+        private bool showSnaps = false;
+        private bool installFabric = false;
+        private bool installForge = false;
+        private bool installOptifine = false;
+        private bool installForgeOptifine = false;
+        private bool installFabricVisible = false;
+        private bool installForgeVisible = false;
+        private bool installOptifineVisible = false;
+        private bool installForgeOptifineVisible = false;
+        private bool downloadJava = false;
+        private bool isControlsEnabled = true;
+        private int progress = 0;
+        private string downloadingFileName;
+
+        Task downloadMinecraftTask;
+
+        Serilog.ILogger logger;
+
+        ObservableCollection<Entity.VersionManifest.Version> filteredVersions;
+        Entity.VersionManifest.Version filteredVersion;
+        VersionManifest versionManifest;
+
+        public VersionsDownloaderViewModel()
+        {
+            logger = Settings.logger;
+            if (FilteredVerions is null)
+            {
+                FilteredVerions = new ObservableCollection<Entity.VersionManifest.Version>();
+            }
+            logger.Debug("Getting versionManifest.json");
+            versionManifest = Downloader.DownloadAndDeserializeJsonData<VersionManifest>("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json");
+            logger.Debug("Updating available versions to download.");
+            updateList();
+        }
+
+        public Entity.VersionManifest.Version FilteredVersion
+        {
+            get { return filteredVersion; }
+            set { 
+                this.RaiseAndSetIfChanged(ref filteredVersion, value);
+
+                InstallFabric = false;
+                InstallForge = false;
+                InstallOptifine = false;
+                InstallForgeOptifine = false;
+
+                using (var httpClient = new HttpClient())
+                {
+                    try
+                    {
+                        var response = httpClient.GetAsync(@$"https://files.veloe.link/launcher/forge/Forge{value.id}/Forge{value.id}.json").Result;
+
+                        if (response.IsSuccessStatusCode)
+                        {
+                            InstallForgeVisible = true;
+                            response = httpClient.GetAsync(@$"https://files.veloe.link/launcher/forge/Forge{value.id}/Optifine{value.id}.jar").Result;
+                            if (response.IsSuccessStatusCode)
+                                InstallForgeOptifineVisible = true;
+                        }
+                        else
+                        {
+                            InstallForgeVisible = false;
+                            InstallForgeOptifineVisible = false;
+                        }
+
+                            response = httpClient.GetAsync(@$"https://files.veloe.link/launcher/fabric/Fabric{value.id}/Fabric{value.id}.json").Result;
+
+                        if (response.IsSuccessStatusCode)
+                            InstallFabricVisible = true;
+                        else
+                            installFabricVisible = false;
+
+                        response = httpClient.GetAsync(@$"https://files.veloe.link/launcher/optifine/Optifine{value.id}/Optifine{value.id}.json").Result;
+
+                        if (response.IsSuccessStatusCode)
+                            InstallOptifineVisible = true;
+                        else
+                            InstallOptifineVisible = false;
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+
+                }
+
+            }
+        }
+
+        public ObservableCollection<Entity.VersionManifest.Version>FilteredVerions
+        {
+            get => filteredVersions; set
+            {
+                this.RaiseAndSetIfChanged(ref filteredVersions, value);
+            }
+        }
+
+        public string StartButton
+        {
+            get => startButton; set
+            {
+                this.RaiseAndSetIfChanged(ref startButton, value);
+            }
+        }
+
+        public bool ShowOld
+        {
+            get { return showOld; }
+            set { 
+                this.RaiseAndSetIfChanged(ref showOld, value);
+                updateList();
+            }
+        }
+
+        public bool ShowSnaps
+        {
+            get { return showSnaps; }
+            set { 
+                this.RaiseAndSetIfChanged(ref showSnaps, value);
+                updateList();
+            }
+        }
+
+        public int Progress
+        {
+            get { return progress; }
+            set
+            {
+                this.RaiseAndSetIfChanged(ref progress, value);
+            }
+        }
+
+        public string DownloadingFileName
+        {
+            get { return downloadingFileName; }
+            set
+            {
+                this.RaiseAndSetIfChanged(ref downloadingFileName, value);
+            }
+        }
+
+        public bool InstallForge
+        {
+            get { return installForge; }
+            set {
+                this.RaiseAndSetIfChanged(ref installForge, value);
+                
+                if (InstallFabric)
+                    InstallFabric = false;
+                }
+        }
+
+        public bool InstallFabric
+        {
+            get { return installFabric; }
+            set { 
+                this.RaiseAndSetIfChanged(ref installFabric, value);
+                if (InstallForge)
+                    InstallForge = false;
+            }
+        }
+
+        public bool InstallOptifine
+        {
+            get { return installOptifine; }
+            set { 
+                this.RaiseAndSetIfChanged(ref installOptifine, value);
+            }
+        }
+
+        public bool InstallForgeOptifine
+        {
+            get { return installForgeOptifine; }
+            set
+            {
+                this.RaiseAndSetIfChanged(ref installForgeOptifine, value);
+                if (value)
+                    InstallForge = true;
+            }
+        }
+
+        public bool InstallForgeVisible
+        {
+            get { return installForgeVisible; }
+            set => this.RaiseAndSetIfChanged(ref installForgeVisible, value);
+        }
+
+        public bool InstallFabricVisible
+        {
+            get { return installFabricVisible; }
+            set => this.RaiseAndSetIfChanged(ref installFabricVisible, value);
+        }
+
+        public bool InstallOptifineVisible
+        {
+            get { return installOptifineVisible; }
+            set => this.RaiseAndSetIfChanged(ref installOptifineVisible, value);
+        }
+
+        public bool InstallForgeOptifineVisible
+        {
+            get { return installForgeOptifineVisible; }
+            set => this.RaiseAndSetIfChanged(ref installForgeOptifineVisible, value);
+        }
+
+        public bool DownloadJava
+        {
+            get { return downloadJava;  }
+            set { this.RaiseAndSetIfChanged(ref downloadJava, value); }
+        }
+
+        public bool IsControlsEnabled
+        {
+            get { return isControlsEnabled; }
+            set { this.RaiseAndSetIfChanged(ref isControlsEnabled, value); }
+        }
+
+        public async Task OnStartBunttonClick()
+        {
+            Task.Run(()=>StartDownload());                     
+        }
+
+        public async Task<TaskStatus> StartDownload()
+        {
+            try
+            {
+                if (FilteredVersion is null)
+                    return TaskStatus.Faulted;
+                IsControlsEnabled = false;
+                logger.Debug("Downloading minecraft {0}", FilteredVersion.id);
+                var webClient = new WebClient();
+
+                logger.Debug("Downloading {0}.json", FilteredVersion.id);
+                var versonJson = Downloader.DownloadAndDeserializeJsonData<Entity.Version.Version>(FilteredVersion.url);
+
+                if (versonJson != null)
+                {
+                    webClient.DownloadProgressChanged += WebClient_DownloadProgressChanged;
+                    //download java
+                    if (DownloadJava)
+                    {
+                        logger.Debug("Getting required Java version.");
+                        if (versonJson.javaVersion.majorVersion != null)
+                        {
+                            string javaUrl = "";
+                            switch (versonJson.javaVersion.majorVersion)
+                            {
+                                case 8:
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsWindows())
+                                        javaUrl = "https://files.veloe.link/launcher/java/8/windows64/java.zip";
+                                    if (!Environment.Is64BitOperatingSystem && OperatingSystem.IsWindows())
+                                        javaUrl = "https://files.veloe.link/launcher/java/8/windows32/java.zip";
+
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsLinux())
+                                        javaUrl = "https://files.veloe.link/launcher/java/8/linux64/java.zip";
+                                    if (!Environment.Is64BitOperatingSystem && OperatingSystem.IsLinux())
+                                        javaUrl = "https://files.veloe.link/launcher/java/8/linux32/java.zip";
+                                    break;
+                                case 16:
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsWindows())
+                                        javaUrl = "https://files.veloe.link/launcher/java/16/windows64/java.zip";
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsLinux())
+                                        javaUrl = "https://files.veloe.link/launcher/java/16/linux64/java.zip";
+                                    break;
+                                case 17:
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsWindows())
+                                        javaUrl = "https://files.veloe.link/launcher/java/17/windows64/java.zip";
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsLinux())
+                                        javaUrl = "https://files.veloe.link/launcher/java/17/linux64/java.zip";
+
+                                    break;
+                                case 18:
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsWindows())
+                                        javaUrl = "https://files.veloe.link/launcher/java/18/windows64/java.zip";
+                                    if (Environment.Is64BitOperatingSystem && OperatingSystem.IsLinux())
+                                        javaUrl = "https://files.veloe.link/launcher/java/18/linux64/java.zip";
+
+                                    break;
+                            }
+                            if (javaUrl != "")
+                            {
+                                logger.Debug("Downloading Java");
+                                DownloadingFileName = "java.zip";
+                                webClient.DownloadFileAsync(new System.Uri(javaUrl), Settings.MinecraftForlderPath + "java.zip");
+                                waitWhileBisy(ref webClient);
+
+                                logger.Debug("Unpacking Java");
+                                DownloadingFileName = "Unpacking java.zip";
+                                ZipFile.ExtractToDirectory($"{Settings.MinecraftForlderPath}java.zip", Settings.MinecraftForlderPath, true);
+                                File.Delete($"{Settings.MinecraftForlderPath}java.zip");
+                            }
+                            else
+                                logger.Debug("Required Java version don't found in json file."); //log that java was not downloaded;
+                        }
+
+                    }
+
+                    //download json
+                    var path = Settings.MinecraftForlderPath + "/versions/" + FilteredVersion.id;
+                    if (!Directory.Exists(path))
+                    {
+                        logger.Debug("Creating path: {0}", path);
+                        Directory.CreateDirectory(path);
+                    }
+
+                    logger.Debug("Downloading {0}", $"{FilteredVersion.id}.json");
+                    DownloadingFileName = $"{FilteredVersion.id}.json";
+                    webClient.DownloadFileAsync(new System.Uri(FilteredVersion.url), path + "/" + FilteredVersion.id + ".json");
+                    waitWhileBisy(ref webClient);
+                    //download jar
+                    if (!Directory.Exists(path))
+                    {
+                        logger.Debug("Creating path: {0}", path);
+                        Directory.CreateDirectory(path);
+                    }
+
+                    logger.Debug("Downloading {0}", $"{FilteredVersion.id}.jar");
+                    DownloadingFileName = $"{FilteredVersion.id}.jar";
+                    webClient.DownloadFileAsync(new System.Uri(versonJson.downloads.client.url), path + "/" + FilteredVersion.id + ".jar");
+                    waitWhileBisy(ref webClient);
+
+                    //download libraries
+                    logger.Debug("Downloading libraries.");
+                    foreach (var library in versonJson.libraries)
+                    {
+                        var libPath = Settings.MinecraftForlderPath + "libraries/";
+                        string[] libPathSplit = new[] { "" };
+                        string libUrl = "";
+                        // getting path and url if universal lib
+                        if (library.natives is null)
+                        {
+                            libPath = Settings.MinecraftForlderPath + "libraries/";
+                            libPathSplit = library.downloads.artifact.path.Split('/');
+                            libUrl = library.downloads.artifact.url;
+                            for (int i = 0; i < libPathSplit.Length - 1; i++)
+                            {
+                                libPath += libPathSplit[i] + "/";
+                            }
+
+                        }
+                        else
+                        {   // getting path if native
+                            libPath = Settings.MinecraftForlderPath + "libraries/";
+                            libPathSplit = new[] { "" };
+                            libUrl = "";
+
+                            if (OperatingSystem.IsWindows() && library.natives.windows is not null)
+                            {
+                                if (library.downloads.classifiers.NativesWindows is not null)
+                                {
+                                    libPathSplit = library.downloads.classifiers.NativesWindows.path.Split('/');
+                                    libUrl = library.downloads.classifiers.NativesWindows.url;
+                                }
+                                else
+                                {
+                                    if (Environment.Is64BitOperatingSystem)
+                                    {
+                                        libPathSplit = library.downloads.classifiers.NativesWindows64.path.Split('/');
+                                        libUrl = library.downloads.classifiers.NativesWindows64.url;
+                                    }
+                                    else
+                                    {
+                                        libPathSplit = library.downloads.classifiers.NativesWindows32.path.Split('/');
+                                        libUrl = library.downloads.classifiers.NativesWindows32.url;
+                                    }
+                                }
+                            }
+                            if (OperatingSystem.IsLinux() && library.natives.linux is not null)
+                            {
+                                libPathSplit = library.downloads.classifiers.NativesLinux.path.Split('/');
+                                libUrl = library.downloads.classifiers.NativesLinux.url;
+                            }
+
+
+                            for (int i = 0; i < libPathSplit.Length - 1; i++)
+                            {
+                                libPath += libPathSplit[i] + "/";
+                            }
+                        }
+
+                        //if no lib url
+                        if (libUrl == String.Empty)
+                            continue;
+
+                        //checking rules
+                        if (library.rules == null)
+                        {
+                            if (!Directory.Exists(libPath))
+                            {
+                                logger.Debug("Creating path: {0}", path);
+                                Directory.CreateDirectory(libPath);
+                            }
+                            logger.Debug("Downloading: {0}", libPathSplit.Last());
+                            DownloadingFileName = libPathSplit.Last();
+                            webClient.DownloadFileAsync(new System.Uri(libUrl), libPath + "/" + libPathSplit.Last());
+                            waitWhileBisy(ref webClient);
+                        }
+                        else
+                        {
+                            foreach (var rule in library.rules)
+                            {
+                                if (rule.action == "allow" && rule.os is null)
+                                {
+                                    if (!Directory.Exists(libPath))
+                                    {
+                                        logger.Debug("Creating path: {0}", path);
+                                        Directory.CreateDirectory(libPath);
+                                    }
+                                    logger.Debug("Downloading: {0}", libPathSplit.Last());
+                                    DownloadingFileName = libPathSplit.Last();
+                                    webClient.DownloadFileAsync(new System.Uri(libUrl), libPath + "/" + libPathSplit.Last());
+                                    waitWhileBisy(ref webClient);
+                                    continue;
+                                }
+                                if (rule.action == "allow" && (rule.os.name == "windows" && OperatingSystem.IsWindows() || rule.os.name == "linux" && OperatingSystem.IsLinux()))
+                                {
+                                    if (!Directory.Exists(libPath))
+                                    {
+                                        logger.Debug("Creating path: {0}", path);
+                                        Directory.CreateDirectory(libPath);
+                                    }
+                                    logger.Debug("Downloading: {0}", libPathSplit.Last());
+                                    DownloadingFileName = libPathSplit.Last();
+                                    webClient.DownloadFileAsync(new System.Uri(libUrl), libPath + "/" + libPathSplit.Last());
+                                    waitWhileBisy(ref webClient);
+                                }
+                            }
+                        }
+                        //unpacking native libs
+                        if (library.natives is not null || library.downloads.classifiers is not null)
+                        {
+                            try
+                            {
+                                if (!Directory.Exists(Settings.MinecraftForlderPath + "versions/" + versonJson.id + "/natives/"))
+                                {
+                                    logger.Debug("Creating path: {0}", Settings.MinecraftForlderPath + "versions/" + versonJson.id + "/natives/");
+                                    Directory.CreateDirectory(Settings.MinecraftForlderPath + "versions/" + versonJson.id + "/natives/");
+                                }
+                                logger.Debug("Extracting {0} to {1}", libPathSplit.Last(), Settings.MinecraftForlderPath + "versions/" + versonJson.id + "/natives/");
+                                ZipFile.ExtractToDirectory(libPath + "/" + libPathSplit.Last(), Settings.MinecraftForlderPath + "versions/" + versonJson.id + "/natives/", true);
+                            }
+                            catch (IOException ex)
+                            {
+                                logger.Error("IOException in VersionsDownloaderViewModel on native lib extraction");
+                            }
+                            //TODO delete META-INF and sha1 files after
+                        }
+                    }
+
+                    var assetsJson = Downloader.DownloadAndDeserializeJsonData<AssetsManifest>(versonJson.assetIndex.url);
+                    var assetsPath = Settings.MinecraftForlderPath + "assets/" + versonJson.assets + "/objects";
+                    var assetsUrl = "https://resources.download.minecraft.net/";
+
+                    //download assets json
+                    logger.Debug("Downloading: {0}", versonJson.assets + ".json");
+                    Downloader.DownloadFile(versonJson.assetIndex.url, Settings.MinecraftForlderPath + "/assets/" + versonJson.assets + "/indexes/", versonJson.assets + ".json");
+
+                    //download assets
+                    foreach (var asset in assetsJson.Objects)
+                    {
+                        var folder = asset.Value.hash.Substring(0, 2);
+                        if (!Directory.Exists(assetsPath + "/" + folder))
+                            Directory.CreateDirectory(assetsPath + "/" + folder);
+
+                        string chksum = String.Empty;
+                        //here hash check
+                        if (File.Exists(assetsPath + "/" + folder + "/" + asset.Value.hash))
+                        {
+                            FileStream fop = File.OpenRead(assetsPath + "/" + folder + "/" + asset.Value.hash);
+                            byte[] hash = System.Security.Cryptography.SHA1.Create().ComputeHash(fop);
+                            chksum = BitConverter.ToString(hash).Replace("-", String.Empty).ToLower();
+
+                            fop.Close();
+                            fop.Dispose();
+                        }
+
+                        if (chksum != asset.Value.hash)
+                        {
+                            logger.Debug("Downloading: {0}", asset.Value.hash);
+                            DownloadingFileName = $"Asset {asset.Value.hash}";
+                            webClient.DownloadFileAsync(new System.Uri(assetsUrl + folder + "/" + asset.Value.hash), assetsPath + "/" + folder + "/" + asset.Value.hash);
+                            waitWhileBisy(ref webClient);
+                        }
+                    }
+
+                    if (InstallForge)
+                    {
+                        var forgePath = $"Forge{versonJson.id}";
+                        var forgeUrl = $"https://files.veloe.link/launcher/forge/Forge{versonJson.id}/";
+
+                        if (!Directory.Exists($"{Settings.MinecraftForlderPath}versions/Forge{versonJson.id}"))
+                        {
+                            logger.Debug("Creating path: {0}", $"{Settings.MinecraftForlderPath}versions/" + forgePath);
+                            Directory.CreateDirectory($"{Settings.MinecraftForlderPath}versions/" + forgePath);
+                        }
+
+                        logger.Debug("Downloading: {0}", $"Forge{versonJson.id}.json");
+                        DownloadingFileName = $"Forge{versonJson.id}.json";
+                        webClient.DownloadFileAsync(new System.Uri($"{forgeUrl}Forge{versonJson.id}.json"), Settings.MinecraftForlderPath + "versions/" + forgePath + "/" + forgePath + ".json");
+                        waitWhileBisy(ref webClient);
+
+                        logger.Debug("Downloading: {0}", "libraries.zip");
+                        DownloadingFileName = "libraries.zip";
+                        webClient.DownloadFileAsync(new System.Uri(forgeUrl + "libraries.zip"), Settings.MinecraftForlderPath + "versions/" + forgePath + "/libraries.zip");
+                        waitWhileBisy(ref webClient);
+
+                        logger.Debug("Extracting: {0}", "libraries.zip");
+                        DownloadingFileName = "Unpacking libraries.zip";
+                        ZipFile.ExtractToDirectory($"{Settings.MinecraftForlderPath}versions/Forge{versonJson.id}/libraries.zip", Settings.MinecraftForlderPath, true);
+                        File.Delete($"{Settings.MinecraftForlderPath}versions/Forge{versonJson.id}/libraries.zip");
+                    }
+
+                    if (InstallOptifine)
+                    {
+                        var optifinePath = $"Optifine{versonJson.id}";
+                        var optifineUrl = $"https://files.veloe.link/launcher/optifine/Optifine{versonJson.id}/";
+
+                        if (!Directory.Exists($"{Settings.MinecraftForlderPath}versions/Optifine{versonJson.id}"))
+                        {
+                            logger.Debug("Creating path: {0}", $"{Settings.MinecraftForlderPath}Optifine/" + optifinePath);
+                            Directory.CreateDirectory($"{Settings.MinecraftForlderPath}Optifine/" + optifinePath);
+                        }
+
+                        logger.Debug("Downloading: {0}", $"Optifine{versonJson.id}.json");
+                        DownloadingFileName = $"Optifine{versonJson.id}.json";
+                        webClient.DownloadFileAsync(new System.Uri($"{optifineUrl}Optifine{versonJson.id}.json"), Settings.MinecraftForlderPath + "versions/" + optifinePath + "/" + optifinePath + ".json");
+                        waitWhileBisy(ref webClient);
+
+                        logger.Debug("Downloading: {0}", $"Optifine{versonJson.id}.json");
+                        DownloadingFileName = $"Optifine{versonJson.id}.json";
+                        webClient.DownloadFileAsync(new System.Uri($"{optifineUrl}Optifine{versonJson.id}.jar"), Settings.MinecraftForlderPath + "versions/" + optifinePath + "/" + optifinePath + ".jar");
+                        waitWhileBisy(ref webClient);
+
+                        logger.Debug("Downloading: {0}", "libraries.zip");
+                        DownloadingFileName = "libraries.zip";
+                        webClient.DownloadFileAsync(new System.Uri(optifineUrl + "libraries.zip"), Settings.MinecraftForlderPath + "versions/" + optifinePath + "/libraries.zip");
+                        waitWhileBisy(ref webClient);
+
+                        logger.Debug("Extracting: {0}", "libraries.zip");
+                        DownloadingFileName = "Unpacking libraries.zip";
+                        ZipFile.ExtractToDirectory($"{Settings.MinecraftForlderPath}versions/Optifine{versonJson.id}/libraries.zip", Settings.MinecraftForlderPath, true);
+                        File.Delete($"{Settings.MinecraftForlderPath}versions/Optifine{versonJson.id}/libraries.zip");
+                    }
+
+                    if (InstallForgeOptifine)
+                    {
+                        if (!Directory.Exists($"{Settings.MinecraftForlderPath}versions/Forge{versonJson.id}/mods"))
+                        {
+                            logger.Debug("Creating path: {0}", $"{Settings.MinecraftForlderPath}versions/Forge" + versonJson.id + "/mods");
+                            Directory.CreateDirectory($"{Settings.MinecraftForlderPath}versions/Forge" + versonJson.id + "/mods");
+                        }
+
+                        logger.Debug("Downloading: {0}", $"Optifine{versonJson.id}.jar");
+                        DownloadingFileName = $"Optifine{versonJson.id}.jar";
+                        webClient.DownloadFileAsync(new System.Uri(@$"https://files.veloe.link/launcher/forge/Forge{versonJson.id}/Optifine{versonJson.id}.jar"),
+                        Settings.MinecraftForlderPath + "versions/Forge" + versonJson.id + "/mods/" + "Optifine" + versonJson.id + ".jar");
+                        waitWhileBisy(ref webClient);
+                    }
+
+                    webClient.DownloadProgressChanged -= WebClient_DownloadProgressChanged;
+                    Progress = 100;
+                    logger.Debug("Downloading finished.");
+                    DownloadingFileName = "Finished!";
+                    IsControlsEnabled = true;
+                }
+
+                webClient.Dispose();
+            }
+            catch (Exception ex)
+            {
+                IsControlsEnabled = true;
+                DownloadingFileName = $"Error occured while downloading {FilteredVersion.id}.";
+                logger.Error(ex.Message);
+                logger.Error(ex.StackTrace);
+                return TaskStatus.Faulted;
+            }
+            return TaskStatus.RanToCompletion;
+        }
+
+        public async void OnStartMcTfcBunttonClick()
+        {
+            Task.Run(() => StartDownloadMcTfc());
+        }
+
+        private async Task<TaskStatus> StartDownloadMcTfc()
+        {
+            try
+            {
+                FilteredVersion = FilteredVerions.Where(x => x.id == "1.12.2").FirstOrDefault();
+
+                if (FilteredVersion is null)
+                    return TaskStatus.Faulted;
+
+
+                InstallForge = true;
+                InstallForgeOptifine = true;
+
+                await StartDownload();
+
+                IsControlsEnabled = false;
+                var mcTfcUrl = $"https://files.veloe.link/launcher/McTFC/McTFC.zip";
+
+                if (!Directory.Exists($"{Settings.MinecraftForlderPath}versions/McTFC"))
+                {
+                    logger.Debug("Creating path: {0}", $"{Settings.MinecraftForlderPath}versions/McTFC");
+                    Directory.CreateDirectory($"{Settings.MinecraftForlderPath}versions/McTFC");
+                }
+
+                WebClient webClient = new WebClient();
+
+                webClient.DownloadProgressChanged += WebClient_DownloadProgressChanged;
+
+                logger.Debug("Downloading: {0}", "McTFC.zip");
+                DownloadingFileName = "McTFC.zip";
+                webClient.DownloadFileAsync(new System.Uri(mcTfcUrl), Settings.MinecraftForlderPath + "versions/McTFC.zip");
+                waitWhileBisy(ref webClient);
+
+                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");
+
+                webClient.DownloadProgressChanged -= WebClient_DownloadProgressChanged;
+                Progress = 100;
+                logger.Debug("Downloading McTFC finished.");
+                DownloadingFileName = "Finished McTFC!";
+                IsControlsEnabled = true;
+                webClient.Dispose();
+
+            }
+            catch (Exception ex)
+            {
+                IsControlsEnabled = true;
+                DownloadingFileName = "Error occured while downloading McTFC.";
+                logger.Error(ex.Message);
+                logger.Error(ex.StackTrace);
+                return TaskStatus.Faulted;
+            }
+            return TaskStatus.RanToCompletion;
+        }
+
+
+        private void waitWhileBisy(ref WebClient webClient)
+        {
+            while(webClient.IsBusy)
+            {
+                //Progress = webClientProgress;
+                Task.Delay(100).Wait();
+            }
+        }
+
+        private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
+        {
+            Progress = e.ProgressPercentage;
+        }
+
+        public void updateList()
+        {
+            FilteredVerions.Clear();
+
+            if (versionManifest.versions is null)
+                return;
+
+            foreach (var version in versionManifest.versions)
+            {
+                if (ShowSnaps && version.type == "snapshot" || ShowOld && version.type is ("old_alpha" or "old_beta") || version.type == "release")    
+                FilteredVerions.Add(version);
+            }
+        }
+
+    }
+}

+ 16 - 0
VeloeMinecraftLauncher/ViewModels/ViewModelBase.cs

@@ -0,0 +1,16 @@
+using ReactiveUI;
+using ReactiveUI.Validation.Extensions;
+using ReactiveUI.Validation.Helpers;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace VeloeMinecraftLauncher.ViewModels
+{
+    public class ViewModelBase : ReactiveValidationObject
+    {
+
+    }
+}

+ 92 - 0
VeloeMinecraftLauncher/Views/MainWindow.axaml

@@ -0,0 +1,92 @@
+<Window xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:vm="using:VeloeMinecraftLauncher.ViewModels"
+        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"
+		Width="800" Height="450"
+        x:Class="VeloeMinecraftLauncher.Views.MainWindow"
+        Icon="/Assets/avalonia-logo.ico"
+        Title="VeloeMinecraftLauncher"
+		CanResize="False">
+
+	
+	
+    <Design.DataContext>
+        <vm:MainWindowViewModel/>
+    </Design.DataContext>
+
+  	
+  <Grid>
+	  <Grid.RowDefinitions>
+	  <RowDefinition Height="*"></RowDefinition>
+	  <RowDefinition Height="40" ></RowDefinition>
+	  <RowDefinition Height="0"></RowDefinition>
+	  </Grid.RowDefinitions>
+	  <Grid.ColumnDefinitions>
+		  <ColumnDefinition Width="*"></ColumnDefinition>
+	  </Grid.ColumnDefinitions>
+	  <Button ZIndex="999" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top" Command="{Binding DownloadUpdate}" IsVisible="{Binding IsUpdateAvailable}">Update Available</Button>
+		<TabControl Grid.Row="0" HorizontalAlignment="Stretch">
+			<TabItem Header="Servers" VerticalContentAlignment="Center">
+				<StackPanel>
+					<TextBlock Text="{Binding McTfcBlock}">
+						  
+					</TextBlock>
+					<TextBlock Text="{Binding McTechBlock}">
+						  
+					</TextBlock>
+					<TextBlock Text="{Binding McVanillaBlock}">
+						  
+					</TextBlock>
+				</StackPanel>
+			</TabItem>
+			<TabItem>
+				<TabItem.Header>
+					<TextBlock VerticalAlignment="Center">Console</TextBlock>
+				</TabItem.Header>
+				<ScrollViewer Name="ConsoleScroll" HorizontalScrollBarVisibility="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
+<TextBlock Text="{Binding ConsoleText}"  VerticalAlignment="Stretch" Width="770" TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Visible"  MaxLines="99999">
+
+</TextBlock>
+				</ScrollViewer>
+			</TabItem>
+			<TabItem>
+				<TabItem.Header>
+					<TextBlock VerticalAlignment="Center">Changelog</TextBlock>
+				</TabItem.Header>
+				<ScrollViewer>
+					<StackPanel>
+						<TextBlock VerticalAlignment="Stretch" TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Visible" Text="При проблемах отправьте мне логи.&#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>
+			</TabItem>
+		</TabControl>
+
+	  <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
+		  
+		  <Button Content="{Binding DownloadButton}" Command="{Binding OnClickCommand}"></Button>
+		  <Button Content="{Binding SettingsButton}" Command="{Binding OpenSettings}" IsVisible="true"></Button>
+		  <TextBlock Margin="3"></TextBlock>
+		  <ComboBox Items="{Binding DownloadedVersions}" SelectedItem="Binding DownloadedVersion" SelectedIndex="{Binding DownloadedIndex}" Width="220" VerticalAlignment="Center" HorizontalAlignment="Center"></ComboBox>
+		  <TextBlock Margin="3"></TextBlock>
+		  <TextBox Text="{Binding Username}" MinWidth="220" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox>
+		  <TextBlock Margin="3"></TextBlock>
+		  <Button Content="{Binding StartButton}" Command="{Binding StartMinecraft}" IsEnabled="{Binding IsNoGameRunning}"></Button>
+	  </StackPanel>
+	  <StackPanel Orientation="Horizontal" Grid.Row="2">
+		  <TextBox Text="{Binding ArgumentsBox}" TextWrapping="Wrap" Width="680" Height="200" IsVisible="false"></TextBox>
+		  <Button Content="{Binding StartButton}" Command="{Binding StartMinecraftCustom}" IsVisible="false"></Button>
+	  </StackPanel>
+  </Grid>
+	
+</Window>

+ 12 - 0
VeloeMinecraftLauncher/Views/MainWindow.axaml.cs

@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace VeloeMinecraftLauncher.Views
+{
+    public partial class MainWindow : Window
+    {
+        public MainWindow()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 47 - 0
VeloeMinecraftLauncher/Views/SettingsWindow.axaml

@@ -0,0 +1,47 @@
+<Window xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+		xmlns:vm="using:VeloeMinecraftLauncher.ViewModels"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="300"
+		Width="600" Height="300"
+        x:Class="VeloeMinecraftLauncher.Views.SettingsWindow"
+        Title="SettingsWindow"
+		CanResize="False">
+
+	<Design.DataContext>
+		<vm:SettingsWindowViewModel/>
+	</Design.DataContext>
+	<Grid Margin="10" ShowGridLines="false">
+		<Grid.RowDefinitions>
+			<RowDefinition Height="*" MaxHeight="35"></RowDefinition>
+			<RowDefinition Height="*" MaxHeight="35"></RowDefinition>
+			<RowDefinition Height="Auto" MinHeight="35" MaxHeight="65" ></RowDefinition>
+			<RowDefinition Height="*" MaxHeight="35"></RowDefinition>
+			<RowDefinition Height="*" MaxHeight="35"></RowDefinition>
+			<RowDefinition Height="*"></RowDefinition>
+		</Grid.RowDefinitions>
+		<Grid.ColumnDefinitions>
+			<ColumnDefinition Width="250"></ColumnDefinition>
+			<ColumnDefinition Width="*"></ColumnDefinition>
+			<ColumnDefinition Width="70"></ColumnDefinition>
+		</Grid.ColumnDefinitions>
+		<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="true" IsEnabled="False">Set path to minecraft folder</CheckBox>
+		<TextBox Grid.Row="0" Grid.Column="1" Margin="5" Text="{Binding MinecraftFolderPath}" IsEnabled="{Binding SetMinecraftFolder}"></TextBox>
+		<Button Grid.Row="0" Grid.Column="2" Content="Open" Command="{Binding OpenMinecraftPathDialog}" CommandParameter="{Binding $parent[Window]}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"></Button>
+		
+		<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding UseCustomJava}">Use custom java</CheckBox>
+		<TextBox Grid.Row="1" Grid.Column="1" Margin="5" Text="{Binding JavaPath}" IsEnabled="{Binding UseCustomJava}"></TextBox>
+		<Button Grid.Row="1" Grid.Column="2" Content="Open" Command="{Binding OpenJavaPathDialog}" CommandParameter="{Binding $parent[Window]}" IsEnabled="{Binding UseCustomJava}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"></Button>
+		
+		<CheckBox Grid.Row="2" Grid.Column="0" IsChecked="{Binding SetMaxRam}">Set max RAM</CheckBox>
+		<TextBox Grid.Row="2" Grid.Column="1" Margin="5" Name="MaxRam" Text="{Binding MaxRam}" IsEnabled="{Binding SetMaxRam}" HorizontalAlignment="Left" VerticalAlignment="Top"></TextBox>
+				
+		<CheckBox Grid.Row="3" Grid.Column="0" IsChecked="{Binding CheckAssets}" IsEnabled="false">Check game assets before start</CheckBox>
+		<CheckBox Grid.Row="4" Grid.ColumnSpan="2" IsChecked="{Binding GameLogToLauncher}">Show game log in launcher (performace issues)</CheckBox>
+		
+		<Button Grid.Row="5" Grid.Column="0" Command="{Binding SaveSettings}" IsEnabled="{Binding IsValid}" Content="Save" VerticalAlignment="Top"></Button>
+		<TextBlock Grid.Row="5" Grid.Column="2" Text="{Binding LauncherVersion}" HorizontalAlignment="Right" VerticalAlignment="Bottom"></TextBlock>
+				
+	</Grid>
+</Window>

+ 12 - 0
VeloeMinecraftLauncher/Views/SettingsWindow.axaml.cs

@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace VeloeMinecraftLauncher.Views
+{
+    public partial class SettingsWindow : Window
+    {
+        public SettingsWindow()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 33 - 0
VeloeMinecraftLauncher/Views/VersionsDownloader.axaml

@@ -0,0 +1,33 @@
+
+<Window xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+		xmlns:vm="using:VeloeMinecraftLauncher.ViewModels"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        mc:Ignorable="d" d:DesignWidth="250" d:DesignHeight="450"
+		Width="250" Height="450"
+        x:Class="VeloeMinecraftLauncher.Views.VersionsDownloader"
+        Title="VersionsDownloader"
+		CanResize="False">
+	
+	<Design.DataContext>
+		<vm:VersionsDownloaderViewModel/>
+	</Design.DataContext>
+	
+	<StackPanel Margin="10" Spacing="5">
+		
+		<ComboBox Items="{Binding FilteredVerions}" SelectedItem="{Binding FilteredVersion}" IsEnabled="{Binding IsControlsEnabled}" HorizontalAlignment="Stretch"></ComboBox>
+		<CheckBox IsChecked="{Binding ShowOld}" IsEnabled="{Binding IsControlsEnabled}">Show old</CheckBox>
+		<CheckBox IsChecked="{Binding ShowSnaps}" IsEnabled="{Binding IsControlsEnabled}">Show snapshots</CheckBox>
+		<CheckBox IsChecked="{Binding DownloadJava}" IsEnabled="{Binding IsControlsEnabled}">Download Java</CheckBox>
+		<CheckBox IsChecked="{Binding InstallFabric}" IsVisible="{Binding InstallFabricVisible}" IsEnabled="{Binding IsControlsEnabled}">Install Fabric</CheckBox>
+		<CheckBox IsChecked="{Binding InstallForge}" IsVisible="{Binding InstallForgeVisible}" IsEnabled="{Binding IsControlsEnabled}">Install Forge</CheckBox>
+		<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}" Command="{Binding OnStartBunttonClick}" IsEnabled="{Binding IsControlsEnabled}"></Button>
+		<Button Content="Install McTFC Client" Command="{Binding OnStartMcTfcBunttonClick}" IsEnabled="{Binding IsControlsEnabled}"></Button>
+		<Button Content="Install McTech Client" Command="{Binding OnClickCommand}" IsEnabled="{Binding IsControlsEnabled}"></Button>
+		<ProgressBar Value="{Binding Progress}" ShowProgressText="true"></ProgressBar>
+		<TextBlock Text="{Binding DownloadingFileName}"></TextBlock>
+	</StackPanel>
+</Window>

+ 12 - 0
VeloeMinecraftLauncher/Views/VersionsDownloader.axaml.cs

@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace VeloeMinecraftLauncher.Views
+{
+    public partial class VersionsDownloader : Window
+    {
+        public VersionsDownloader()
+        {
+            InitializeComponent();
+        }
+    }
+}