Browse Source

small versions downloader viewmodel refactoring

Veloe 2 years ago
parent
commit
13117b4b51

+ 5 - 7
VeloeMinecraftLauncher/Utils/StartCommandBuilder.cs

@@ -71,7 +71,7 @@ internal static class StartCommandBuilder
             {          
                 var libPath = GetLibPathFromName(library.Name);
                 if (rulesVaild)
-                    returnString.Append(Path.GetFullPath(Settings.minecraftForlderPath + "libraries/" + libPath +".jar") + separator);
+                    returnString.Append(Path.GetFullPath(Settings.minecraftForlderPath + "libraries/" + libPath) + separator);
 
                 continue;
             }
@@ -381,7 +381,7 @@ internal static class StartCommandBuilder
         return returnString.ToString();
     }
 
-    public static string GetLibPathFromName(string name, bool extention = false)
+    public static string GetLibPathFromName(string name)
     {
         Debug.WriteLine($"GameLibPathFromName: {name}");
         var dirs = name.Split(':');
@@ -392,16 +392,14 @@ internal static class StartCommandBuilder
             libPath += dir + "/";
         }
 
-        libPath += dirs[dirs.Length - 2] + "-" + dirs[dirs.Length - 1];
-
-        if (extention)
-            libPath += ".jar";
+        libPath += dirs[dirs.Length - 2] + "-" + dirs[dirs.Length - 1] + ".jar";
 
+#if DEBUG
         if (File.Exists(Settings.minecraftForlderPath + "libraries/" + libPath))
             Debug.WriteLine($"Lib exists: {Settings.minecraftForlderPath + "libraries/" + libPath}");
         else
             Debug.WriteLine($"Lib not exists: {Settings.minecraftForlderPath + "libraries/" + libPath}");
-
+#endif
         return libPath;
     }
 }

+ 2 - 1
VeloeMinecraftLauncher/ViewModels/MainWindowViewModel.cs

@@ -391,9 +391,10 @@ public class MainWindowViewModel : ViewModelBase
 
     public void OnClickCommand()
     {
+        using var versionsDownloaderViewModel = new VersionsDownloaderViewModel();
         var versionsDownloader = new VersionsDownloader 
         { 
-            DataContext = new VersionsDownloaderViewModel() 
+            DataContext = versionsDownloaderViewModel
         };
        
         if (Avalonia.Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)

+ 434 - 404
VeloeMinecraftLauncher/ViewModels/VersionsDownloaderViewModel.cs

@@ -19,7 +19,7 @@ using System.Linq.Expressions;
 
 namespace VeloeMinecraftLauncher.ViewModels;
 
-public class VersionsDownloaderViewModel : ViewModelBase
+public class VersionsDownloaderViewModel : ViewModelBase, IDisposable
 {
     private string _downloadButtonText = "Download";
     private bool _showOld = false;
@@ -65,6 +65,10 @@ public class VersionsDownloaderViewModel : ViewModelBase
     public VersionsDownloaderViewModel()
     {
         IsControlsEnabled = false;
+
+        this.PropertyChanged += OnFilteredVersionPropertyChanged;
+        this.PropertyChanged += OnDownloadedVersionPropertyChanged;
+
         try
         {
             _logger = Settings.logger;
@@ -105,91 +109,13 @@ public class VersionsDownloaderViewModel : ViewModelBase
     public Entity.VersionManifest.Version FilteredVersion
     {
         get { return _filteredVersion; }
-        set { 
-            this.RaiseAndSetIfChanged(ref _filteredVersion, value);
-
+        set 
+        {
             InstallFabric = false;
             InstallForge = false;
             InstallOptifine = false;
             InstallForgeOptifine = false;
-
-            if (value is null)
-                return;
-
-            if (value.Type == "modpack")
-            {
-                try
-                {
-                    if (System.IO.File.Exists(Settings.minecraftForlderPath + $"versions/{value.Id}/revision.json"))
-                    {
-                        if (value.ComplianceLevel > JsonSerializer.Deserialize<int>(System.IO.File.ReadAllText(Settings.minecraftForlderPath + $"versions/{value.Id}/revision.json")))
-                            DownloadButtonText = "Update Modpack";
-                        else
-                            DownloadButtonText = "Reinstall Modpack";
-                    }
-                    else
-                        if (System.IO.Directory.Exists($"{Settings.minecraftForlderPath}versions/{value.Id}") && System.IO.File.Exists($"{Settings.minecraftForlderPath}versions/{value.Id}/{value.Id}.json"))
-                        DownloadButtonText = "Update Modpack";
-                    else
-                        DownloadButtonText = "Download Modpack";
-                }
-                catch (Exception)
-                {
-                    DownloadButtonText = "Update Modpack";
-                }
-            }
-            else
-            {
-                if (System.IO.File.Exists(Settings.minecraftForlderPath + $"versions/{value.Id}/{value.Id}.json"))
-                    DownloadButtonText = "Reinstall";
-                else
-                    DownloadButtonText = "Download";
-            }
-
-            try
-            {
-                Task.Run(() =>
-                {
-                    _filteredVersionTokenSource.Cancel();
-                    _filteredVersionTokenSource.Dispose();
-                    _filteredVersionTokenSource = new();
-                    try
-                    {
-                        if (Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/forge/Forge{value.Id}/Forge{value.Id}.json", _filteredVersionTokenSource.Token).Result)
-                        {
-                            if (Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/forge/Forge{value.Id}/Optifine{value.Id}.jar", _filteredVersionTokenSource.Token).Result)
-                                InstallForgeOptifineVisible = true;
-                            InstallForgeVisible = true;
-                        }
-                        else
-                        {
-                            InstallForgeVisible = false;
-                            InstallForgeOptifineVisible = false;
-                        }
-
-                        if (Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/fabric/Fabric{value.Id}/Fabric{value.Id}.json", _filteredVersionTokenSource.Token).Result)
-                            InstallFabricVisible = true;
-                        else
-                            InstallFabricVisible = false;
-
-                        if (Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/optifine/Optifine{value.Id}/Optifine{value.Id}.json", _filteredVersionTokenSource.Token).Result)
-                            InstallOptifineVisible = true;
-                        else
-                            InstallOptifineVisible = false;
-                    }
-                    catch (OperationCanceledException)
-                    {
-                        InstallForgeVisible = false;
-                        InstallForgeOptifineVisible = false;
-                        InstallFabricVisible = false;
-                        InstallOptifineVisible = false;
-                    }
-                });   
-            }
-            catch (Exception ex)
-            {
-                OpenErrorWindow(ex);
-            }
+            this.RaiseAndSetIfChanged(ref _filteredVersion, value);
         }
     }
 
@@ -199,330 +125,9 @@ public class VersionsDownloaderViewModel : ViewModelBase
         set 
         { 
             this.RaiseAndSetIfChanged(ref _downloadedVersion, value);
-            Task.Run(() =>
-            {
-                try
-                {
-                    IsControlsEnabled = false;
-                    DownloadedVersionTree.Clear();
-                    DownloadedVersionTree = new();
-                    this.RaisePropertyChanged(nameof(DownloadedVersionTree));
-                    DownloadedVersionsDictionary.Clear();
-
-                    if (value is null)
-                    {
-                        IsControlsEnabled = true;
-                        return;
-                    }
-
-                    Entity.Version.Version? version = null;
-
-                    foreach (var dversion in DownloadedVersions)
-                    {
-                        string json;
-                        using (StreamReader reader = new StreamReader(dversion.path))
-                        {
-                            json = reader.ReadToEnd();
-                        }
-
-                        var versionObject = JsonSerializer.Deserialize<Entity.Version.Version>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
-                        DownloadedVersionsDictionary.Add(dversion.version, versionObject);
-
-                        if (dversion == _downloadedVersion)
-                        {
-                            version = versionObject;
-                        }
-                    }
-
-                    if (version is null)
-                    {
-                        OpenErrorWindow("Json file is invalid!");
-                        return;
-                    }
-
-                    var versionDir = new FileInfo(value.path)?.Directory?.FullName;
-
-                    if (versionDir is null)
-                    {
-                        OpenErrorWindow("Version folder is invalid!");
-                        return;
-                    }
-
-                    //get version
-
-                    DownloadedVersionTree.Add(new TreeNode() { Title = "Version: " + (version.InheritsFrom ?? version.Id) });
-
-                    //calc Size
-
-                    var allSize = 0L;
-                    var dirSize = 0L;
-                    var dirInfo = new DirectoryInfo(versionDir);
-                    var sizeTreeNode = new TreeNode();
-                    var dirTreeNode = new TreeNode();
-
-                    dirTreeNode.SubNode.AddRange(DirectoryToTreeNode(dirInfo,out dirSize));
-                    dirTreeNode.Title = "versions/" + dirInfo.Name + " (" + BytesToString(dirSize) + ")";
-                    sizeTreeNode.SubNode.Add(dirTreeNode);
-                    allSize += dirSize;
-
-                    if (version.InheritsFrom is null)
-                    {
-                        dirInfo = new DirectoryInfo(Settings.minecraftForlderPath);
-                        sizeTreeNode.SubNode.AddRange(
-                            DirectoryToTreeNode(
-                                dirInfo,
-                                out dirSize,
-                                (dir) => dir.Name == ".mixin.out" ||
-                                    dir.Name == "assets" ||
-                                    dir.Name == "javaruntime" ||
-                                    dir.Name == "libraries" ||
-                                    dir.Name == "logs" ||
-                                    dir.Name == "versions",
-                                (file) => file.Name.Contains("Veloe") ||
-                                    file.Name.Contains("log") ||
-                                    file.Name.Contains("exe") ||
-                                    file.Name == "launcher_profiles.json" ||
-                                    file.Name == "libHarfBuzzSharp.dll" ||
-                                    file.Name == "libSkiaSharp.dll" ||
-                                    file.Name == "settings.json")
-                            );
-                        allSize += dirSize;
-                    }
-
-                    sizeTreeNode.Title = "Size: " + BytesToString(allSize); 
-                    DownloadedVersionTree.Add(sizeTreeNode);
-
-                    //calc Worlds
-
-                    dirInfo = null;
-
-                    if (version.InheritsFrom is null && Directory.Exists(Settings.minecraftForlderPath + "saves"))
-                        dirInfo = new DirectoryInfo(Settings.minecraftForlderPath + "saves");
-                    else
-                        if (Directory.Exists(versionDir + "/saves"))
-                        dirInfo = new DirectoryInfo(versionDir + "/saves");
-
-                    if (dirInfo is not null)
-                    {
-                        var worldsTreeNode = new TreeNode() { Title = "Worlds: " + dirInfo.GetDirectories().Count() };
-
-                        foreach (var world in dirInfo.GetDirectories())
-                        { worldsTreeNode.SubNode.Add(new TreeNode() { Title = world.Name }); }
-
-                        DownloadedVersionTree.Add(worldsTreeNode);
-                    }
-
-                    //check modloader
-
-                    var modsTreeNode = new TreeNode();
-
-                    if (version.InheritsFrom is null)
-                        modsTreeNode.Title = "Modloader: No";
-                    else if (version.Libraries.Any(l => l.Downloads?.Artifact?.Url?.Contains("forge") ?? false))
-                    {
-                        modsTreeNode.Title = "Modloader: Forge";
-                        OpenErrorWindow("This version contains forge modloader, it installs some libraries that can't be displayed and deleted in current launcher versions manager.");
-                    }
-                    else if (version.Libraries.Any(l => l.Name.Contains("fabric")))
-                        modsTreeNode.Title = "Modloader: Fabric";
-
-                    //get mods list
-
-                    if (modsTreeNode.Title != "No" && Directory.Exists(versionDir + "/mods"))
-                    {
-                        dirInfo = new DirectoryInfo(versionDir + "/mods");
-
-                        modsTreeNode.Title += " (" + dirInfo.EnumerateFiles("*.jar", SearchOption.TopDirectoryOnly).Count() + ")";
-                        foreach (var mod in dirInfo.EnumerateFiles("*.jar", SearchOption.TopDirectoryOnly))
-                        { modsTreeNode.SubNode.Add(new TreeNode() { Title = mod.Name }); }
-
-                    }
-
-                    DownloadedVersionTree.Add(modsTreeNode);
-
-                    //calc resourcepacks
-
-                    dirInfo = null;
-
-                    if (version.InheritsFrom is null && Directory.Exists(Settings.minecraftForlderPath + "resourcepacks"))
-                        dirInfo = new DirectoryInfo(Settings.minecraftForlderPath + "resourcepacks");
-                    else
-                        if (Directory.Exists(versionDir + "/resourcepacks"))
-                        dirInfo = new DirectoryInfo(versionDir + "/resourcepacks");
-
-                    if (dirInfo is not null)
-                    {
-                        var resourcepacksTreeNode = new TreeNode();
-
-                        resourcepacksTreeNode.Title = "Resoursepacks: " + (dirInfo?.GetDirectories()?.Count() + dirInfo?.GetFiles().Count() ?? 0).ToString();
-                        foreach (var resourcepack in dirInfo?.GetFileSystemInfos())
-                        { resourcepacksTreeNode.SubNode.Add(new TreeNode() { Title = resourcepack.Name }); }
-
-                        DownloadedVersionTree.Add(resourcepacksTreeNode);
-                    }
-
-                    //calc assets
-
-                    var assetsFolderName = version.Assets;
-
-                    if (version.Assets is null &&
-                        version.InheritsFrom is not null &&
-                        DownloadedVersionsDictionary.TryGetValue(version.InheritsFrom, out var outVersion) &&
-                        outVersion?.Assets is not null)
-                    {
-                        assetsFolderName = outVersion.Assets;
-                    }
-
-                    if (Directory.Exists(Settings.minecraftForlderPath + "assets/" + assetsFolderName))
-                    {
-                        dirInfo = new DirectoryInfo(Settings.minecraftForlderPath + "assets/" + assetsFolderName);
-                        allSize = 0;
-                        foreach (var file in dirInfo.EnumerateFiles("*", SearchOption.AllDirectories))
-                        { allSize += file.Length; }
-
-                        var assetsTreeNode = new TreeNode();
-                        var usedByVersionCount = 0;
-
-                        foreach (var dictionaryVersion in DownloadedVersionsDictionary)
-                        {
-                            if ((dictionaryVersion.Value?.Assets == assetsFolderName &&
-                                 dictionaryVersion.Value?.InheritsFrom is null) ||
-                                (dictionaryVersion.Value?.InheritsFrom is not null &&
-                                 DownloadedVersionsDictionary.TryGetValue(dictionaryVersion.Value.InheritsFrom, out outVersion) &&
-                                 outVersion?.Assets == assetsFolderName))
-                            {
-                                usedByVersionCount++;
-                                assetsTreeNode.SubNode.Add(new TreeNode() { Title = dictionaryVersion.Key });
-                            }
-                        }
-
-                        assetsTreeNode.Title = "Assets: " + version.Assets + " (" + BytesToString(allSize) + ") (" + usedByVersionCount + ")";
-                        DownloadedVersionTree.Add(assetsTreeNode);
-                    }
-
-                    // calc libraries
-
-                    var usedLibraries = version.Libraries;
-
-                    var libTreeNode = new TreeNode();
-                    libTreeNode.Title = "Libraries";
-
-                    if (version.InheritsFrom is not null && DownloadedVersionsDictionary.TryGetValue(version.InheritsFrom, out outVersion) && outVersion is not null)
-                    {
-                        usedLibraries.AddRange(outVersion.Libraries);
-                    }
-
-                    usedLibraries = 
-                        usedLibraries
-                            .Where(l =>
-                                File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Artifact?.Path)) ||
-                                File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesLinux?.Path)) ||
-                                File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesWindows?.Path)) ||
-                                File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesWindows64?.Path)) ||
-                                File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesWindows32?.Path)) ||
-                                File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + StartCommandBuilder.GetLibPathFromName(l.Name, true)))
-                            )
-                            .ToList();
-
-                    var libraries = GetLibrariesFromVersions(DownloadedVersionsDictionary)                       
-                        .Where(v => usedLibraries.Any(u => v.Value.Name == u.Name))                      
-                        .GroupBy(a => a.Value.Name)
-                        .Select(g =>
-                        {
-                            return new { 
-                                Name = g.Key, 
-                                Count = g.Select(a => a.Key).Distinct().Count(), 
-                                Versions = g.Select(a => a.Key).Distinct(), 
-                                LibraryUniqueInstances = g.Select(a => a.Value).Distinct() //IEnumerable cause there can be several lib blocks with different rules in one version json
-                            };
-                        });
-
-                    foreach (var lib in libraries)
-                    {
-                        var subLibTreeNode = new TreeNode() 
-                        { 
-                            Title = lib.Name + " (" + lib.Count + ")", 
-                        };
-
-                        //if only one version uses it (selected version), then add path to lib in Tag for deletion
-                        if (lib.Count == 1)
-                        {
-                            subLibTreeNode.Tag = lib.LibraryUniqueInstances.Where(l => l.Downloads?.Artifact?.Path is not null).Select(l => l.Downloads?.Artifact?.Path).FirstOrDefault() ?? string.Empty;
-                            //if fabric library
-                            if (File.Exists(Settings.minecraftForlderPath + "libraries/" + StartCommandBuilder.GetLibPathFromName(lib.Name,true)))
-                                subLibTreeNode.Tag = StartCommandBuilder.GetLibPathFromName(lib.Name,true);
-                        }
-
-                        var subLibUsedVersionsTreeNodeHeader = new TreeNode() { Title = "Using in versions:" };
-
-                        foreach (var ver in lib.Versions)
-                            subLibUsedVersionsTreeNodeHeader.SubNode.Add(new TreeNode() { Title = ver });
-
-                        if (subLibUsedVersionsTreeNodeHeader.SubNode.Count > 0)
-                            subLibTreeNode.SubNode.Add(subLibUsedVersionsTreeNodeHeader);
-
-                        if (lib.LibraryUniqueInstances.Any(l=>l.Natives is not null) || lib.LibraryUniqueInstances.Any(l=>l.Downloads?.Classifiers is not null))
-                        {
-                            var subLibNativesTreeNodeHeader = new TreeNode() { Title = "Natives:" };
-
-                            //check classifiers
-                            var libraryNativeInstance = lib.LibraryUniqueInstances.Where(l => l.Natives is not null || l.Downloads?.Classifiers is not null).First();
-
-                            if (libraryNativeInstance.Downloads?.Classifiers?.NativesWindows is not null)
-                            {
-                                subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode() 
-                                { 
-                                    Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesWindows.Path) ?? "No lib name", 
-                                    Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesWindows?.Path ?? string.Empty : string.Empty
-                                });
-                            }
-
-                            if (libraryNativeInstance.Downloads?.Classifiers?.NativesWindows32 is not null)
-                            {
-                                subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode() 
-                                { 
-                                    Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesWindows32.Path) ?? "No lib name",
-                                    Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesWindows32?.Path ?? string.Empty : string.Empty
-                                });
-                            }
-
-                            if (libraryNativeInstance.Downloads?.Classifiers?.NativesWindows64 is not null)
-                            {
-                                subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode()
-                                {
-                                    Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesWindows64.Path) ?? "No lib name",
-                                    Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesWindows64?.Path ?? string.Empty : string.Empty
-                                });
-                            }
-
-                            if (libraryNativeInstance.Downloads?.Classifiers?.NativesLinux is not null)
-                            {
-                                subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode() 
-                                {
-                                    Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesLinux.Path) ?? "No lib name", 
-                                    Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesLinux?.Path ?? string.Empty : string.Empty 
-                                });
-                            }
-
-                            if (subLibNativesTreeNodeHeader.SubNode.Count > 0)
-                                subLibTreeNode.SubNode.Add(subLibNativesTreeNodeHeader);
-
-                        }
-
-                        libTreeNode.SubNode.Add(subLibTreeNode);
-                    }
-
-                    _libraries = libTreeNode;
-                    DownloadedVersionTree.Add(libTreeNode);
-                }
-                finally
-                {
-                    this.RaisePropertyChanged(nameof(DownloadedVersionTree));
-                    IsControlsEnabled = true;
-                }
-            });
         }
     }
+
     public Dictionary<string,Entity.Version.Version?> DownloadedVersionsDictionary { get; set; }
 
     public ObservableCollection<Entity.VersionManifest.Version> FilteredVersions
@@ -667,6 +272,102 @@ public class VersionsDownloaderViewModel : ViewModelBase
         set => this.RaiseAndSetIfChanged(ref _isControlsEnabled, value);
     }
 
+    private async void OnFilteredVersionPropertyChanged(object? sender, PropertyChangedEventArgs e)
+    {
+        if (e.PropertyName is nameof(FilteredVersion))
+        { 
+            await DownloadButtonTextUpdateAsync(FilteredVersion);
+            await GetFilteredVersionDownloadableFeaturesAsync(FilteredVersion);
+        }
+    }
+
+    private async void OnDownloadedVersionPropertyChanged(object? sender, PropertyChangedEventArgs e)
+    {
+        if (e.PropertyName is nameof(DownloadedVersion))
+        {
+            await CreateNewTreeListAsync(DownloadedVersion);
+        }
+    }
+
+    private Task DownloadButtonTextUpdateAsync(Entity.VersionManifest.Version value)
+    {
+        if (value is null)
+            return Task.CompletedTask;
+
+        if (value.Type == "modpack")
+        {
+            try
+            {
+                if (System.IO.File.Exists(Settings.minecraftForlderPath + $"versions/{value.Id}/revision.json"))
+                {
+                    if (value.ComplianceLevel > JsonSerializer.Deserialize<int>(System.IO.File.ReadAllText(Settings.minecraftForlderPath + $"versions/{value.Id}/revision.json")))
+                        DownloadButtonText = "Update Modpack";
+                    else
+                        DownloadButtonText = "Reinstall Modpack";
+                }
+                else
+                    if (System.IO.Directory.Exists($"{Settings.minecraftForlderPath}versions/{value.Id}") && System.IO.File.Exists($"{Settings.minecraftForlderPath}versions/{value.Id}/{value.Id}.json"))
+                    DownloadButtonText = "Update Modpack";
+                else
+                    DownloadButtonText = "Download Modpack";
+            }
+            catch (Exception)
+            {
+                DownloadButtonText = "Update Modpack";
+            }
+        }
+        else
+        {
+            if (System.IO.File.Exists(Settings.minecraftForlderPath + $"versions/{value.Id}/{value.Id}.json"))
+                DownloadButtonText = "Reinstall";
+            else
+                DownloadButtonText = "Download";
+        }
+        return Task.CompletedTask;
+    }
+
+    private async Task GetFilteredVersionDownloadableFeaturesAsync(Entity.VersionManifest.Version value)
+    {
+        _filteredVersionTokenSource.Cancel();
+        _filteredVersionTokenSource.Dispose();
+        _filteredVersionTokenSource = new();
+        try
+        {
+            if (await Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/forge/Forge{value.Id}/Forge{value.Id}.json", _filteredVersionTokenSource.Token))
+            {
+                if (await Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/forge/Forge{value.Id}/Optifine{value.Id}.jar", _filteredVersionTokenSource.Token))
+                    InstallForgeOptifineVisible = true;
+                InstallForgeVisible = true;
+            }
+            else
+            {
+                InstallForgeVisible = false;
+                InstallForgeOptifineVisible = false;
+            }
+
+            if (await Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/fabric/Fabric{value.Id}/Fabric{value.Id}.json", _filteredVersionTokenSource.Token))
+                InstallFabricVisible = true;
+            else
+                InstallFabricVisible = false;
+
+            if (await Downloader.IsFileAvaliable(@$"https://files.veloe.link/launcher/optifine/Optifine{value.Id}/Optifine{value.Id}.json", _filteredVersionTokenSource.Token))
+                InstallOptifineVisible = true;
+            else
+                InstallOptifineVisible = false;
+        }
+        catch (OperationCanceledException)
+        {
+            InstallForgeVisible = false;
+            InstallForgeOptifineVisible = false;
+            InstallFabricVisible = false;
+            InstallOptifineVisible = false;
+        }  
+        catch (Exception ex)
+        {
+            OpenErrorWindow(ex);
+        }
+    }
+
     public async Task OnStartBunttonClick()
     {
         await Task.Run(async () => {
@@ -723,6 +424,329 @@ public class VersionsDownloaderViewModel : ViewModelBase
         });
     }
 
+    private async Task CreateNewTreeListAsync(DownloadedVersion value)
+    {
+        await Task.Run(async() =>
+        {
+            try
+            {
+                IsControlsEnabled = false;
+                DownloadedVersionTree.Clear();
+                DownloadedVersionTree = new();
+                this.RaisePropertyChanged(nameof(DownloadedVersionTree));
+                DownloadedVersionsDictionary.Clear();
+
+                if (value is null)
+                {
+                    IsControlsEnabled = true;
+                    return;
+                }
+
+                Entity.Version.Version? version = null;
+
+                foreach (var dversion in DownloadedVersions)
+                {
+                    using StreamReader reader = new StreamReader(dversion.path);
+
+                    Entity.Version.Version versionObject = await JsonSerializer.DeserializeAsync<Entity.Version.Version>(reader.BaseStream, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+                    DownloadedVersionsDictionary.Add(dversion.version, versionObject);
+
+                    if (dversion == _downloadedVersion)
+                    {
+                        version = versionObject;
+                    }
+                }
+
+                if (version is null)
+                {
+                    OpenErrorWindow("Json file is invalid!");
+                    return;
+                }
+
+                var versionDir = new FileInfo(value.path)?.Directory?.FullName;
+
+                if (versionDir is null)
+                {
+                    OpenErrorWindow("Version folder is invalid!");
+                    return;
+                }
+
+                //get version
+
+                DownloadedVersionTree.Add(new TreeNode() { Title = "Version: " + (version.InheritsFrom ?? version.Id) });
+
+                //calc Size
+
+                var allSize = 0L;
+                var dirSize = 0L;
+                var dirInfo = new DirectoryInfo(versionDir);
+                var sizeTreeNode = new TreeNode();
+                var dirTreeNode = new TreeNode();
+
+                dirTreeNode.SubNode.AddRange(DirectoryToTreeNode(dirInfo, out dirSize));
+                dirTreeNode.Title = "versions/" + dirInfo.Name + " (" + BytesToString(dirSize) + ")";
+                sizeTreeNode.SubNode.Add(dirTreeNode);
+                allSize += dirSize;
+
+                if (version.InheritsFrom is null)
+                {
+                    dirInfo = new DirectoryInfo(Settings.minecraftForlderPath);
+                    sizeTreeNode.SubNode.AddRange(
+                        DirectoryToTreeNode(
+                            dirInfo,
+                            out dirSize,
+                            (dir) => dir.Name == ".mixin.out" ||
+                                dir.Name == "assets" ||
+                                dir.Name == "javaruntime" ||
+                                dir.Name == "libraries" ||
+                                dir.Name == "logs" ||
+                                dir.Name == "versions",
+                            (file) => file.Name.Contains("Veloe") ||
+                                file.Name.Contains("log") ||
+                                file.Name.Contains("exe") ||
+                                file.Name == "launcher_profiles.json" ||
+                                file.Name == "libHarfBuzzSharp.dll" ||
+                                file.Name == "libSkiaSharp.dll" ||
+                                file.Name == "settings.json")
+                        );
+                    allSize += dirSize;
+                }
+
+                sizeTreeNode.Title = "Size: " + BytesToString(allSize);
+                DownloadedVersionTree.Add(sizeTreeNode);
+
+                //calc Worlds
+
+                dirInfo = null;
+
+                if (version.InheritsFrom is null && Directory.Exists(Settings.minecraftForlderPath + "saves"))
+                    dirInfo = new DirectoryInfo(Settings.minecraftForlderPath + "saves");
+                else
+                    if (Directory.Exists(versionDir + "/saves"))
+                    dirInfo = new DirectoryInfo(versionDir + "/saves");
+
+                if (dirInfo is not null)
+                {
+                    var worldsTreeNode = new TreeNode() { Title = "Worlds: " + dirInfo.GetDirectories().Count() };
+
+                    foreach (var world in dirInfo.GetDirectories())
+                    { worldsTreeNode.SubNode.Add(new TreeNode() { Title = world.Name }); }
+
+                    DownloadedVersionTree.Add(worldsTreeNode);
+                }
+
+                //check modloader
+
+                var modsTreeNode = new TreeNode();
+
+                if (version.InheritsFrom is null)
+                    modsTreeNode.Title = "Modloader: No";
+                else if (version.Libraries.Any(l => l.Downloads?.Artifact?.Url?.Contains("forge") ?? false))
+                {
+                    modsTreeNode.Title = "Modloader: Forge";
+                    OpenErrorWindow("This version contains forge modloader, it installs some libraries that can't be displayed and deleted in current launcher versions manager.");
+                }
+                else if (version.Libraries.Any(l => l.Name.Contains("fabric")))
+                    modsTreeNode.Title = "Modloader: Fabric";
+
+                //get mods list
+
+                if (modsTreeNode.Title != "No" && Directory.Exists(versionDir + "/mods"))
+                {
+                    dirInfo = new DirectoryInfo(versionDir + "/mods");
+
+                    modsTreeNode.Title += " (" + dirInfo.EnumerateFiles("*.jar", SearchOption.TopDirectoryOnly).Count() + ")";
+                    foreach (var mod in dirInfo.EnumerateFiles("*.jar", SearchOption.TopDirectoryOnly))
+                    { modsTreeNode.SubNode.Add(new TreeNode() { Title = mod.Name }); }
+
+                }
+
+                DownloadedVersionTree.Add(modsTreeNode);
+
+                //calc resourcepacks
+
+                dirInfo = null;
+
+                if (version.InheritsFrom is null && Directory.Exists(Settings.minecraftForlderPath + "resourcepacks"))
+                    dirInfo = new DirectoryInfo(Settings.minecraftForlderPath + "resourcepacks");
+                else
+                    if (Directory.Exists(versionDir + "/resourcepacks"))
+                    dirInfo = new DirectoryInfo(versionDir + "/resourcepacks");
+
+                if (dirInfo is not null)
+                {
+                    var resourcepacksTreeNode = new TreeNode();
+
+                    resourcepacksTreeNode.Title = "Resoursepacks: " + (dirInfo?.GetDirectories()?.Count() + dirInfo?.GetFiles().Count() ?? 0).ToString();
+                    foreach (var resourcepack in dirInfo?.GetFileSystemInfos())
+                    { resourcepacksTreeNode.SubNode.Add(new TreeNode() { Title = resourcepack.Name }); }
+
+                    DownloadedVersionTree.Add(resourcepacksTreeNode);
+                }
+
+                //calc assets
+
+                var assetsFolderName = version.Assets;
+
+                if (version.Assets is null &&
+                    version.InheritsFrom is not null &&
+                    DownloadedVersionsDictionary.TryGetValue(version.InheritsFrom, out var outVersion) &&
+                    outVersion?.Assets is not null)
+                {
+                    assetsFolderName = outVersion.Assets;
+                }
+
+                if (Directory.Exists(Settings.minecraftForlderPath + "assets/" + assetsFolderName))
+                {
+                    dirInfo = new DirectoryInfo(Settings.minecraftForlderPath + "assets/" + assetsFolderName);
+                    allSize = 0;
+                    foreach (var file in dirInfo.EnumerateFiles("*", SearchOption.AllDirectories))
+                    { allSize += file.Length; }
+
+                    var assetsTreeNode = new TreeNode();
+                    var usedByVersionCount = 0;
+
+                    foreach (var dictionaryVersion in DownloadedVersionsDictionary)
+                    {
+                        if ((dictionaryVersion.Value?.Assets == assetsFolderName &&
+                             dictionaryVersion.Value?.InheritsFrom is null) ||
+                            (dictionaryVersion.Value?.InheritsFrom is not null &&
+                             DownloadedVersionsDictionary.TryGetValue(dictionaryVersion.Value.InheritsFrom, out outVersion) &&
+                             outVersion?.Assets == assetsFolderName))
+                        {
+                            usedByVersionCount++;
+                            assetsTreeNode.SubNode.Add(new TreeNode() { Title = dictionaryVersion.Key });
+                        }
+                    }
+
+                    assetsTreeNode.Title = "Assets: " + version.Assets + " (" + BytesToString(allSize) + ") (" + usedByVersionCount + ")";
+                    DownloadedVersionTree.Add(assetsTreeNode);
+                }
+
+                // calc libraries
+
+                var usedLibraries = version.Libraries;
+
+                var libTreeNode = new TreeNode();
+                libTreeNode.Title = "Libraries";
+
+                if (version.InheritsFrom is not null && DownloadedVersionsDictionary.TryGetValue(version.InheritsFrom, out outVersion) && outVersion is not null)
+                {
+                    usedLibraries.AddRange(outVersion.Libraries);
+                }
+
+                usedLibraries =
+                    usedLibraries
+                        .Where(l =>
+                            File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Artifact?.Path)) ||
+                            File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesLinux?.Path)) ||
+                            File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesWindows?.Path)) ||
+                            File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesWindows64?.Path)) ||
+                            File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + l.Downloads?.Classifiers?.NativesWindows32?.Path)) ||
+                            File.Exists(Path.Combine(Settings.minecraftForlderPath + "libraries/" + StartCommandBuilder.GetLibPathFromName(l.Name)))
+                        )
+                        .ToList();
+
+                var libraries = GetLibrariesFromVersions(DownloadedVersionsDictionary)
+                    .Where(v => usedLibraries.Any(u => v.Value.Name == u.Name))
+                    .GroupBy(a => a.Value.Name)
+                    .Select(g =>
+                    {
+                        return new
+                        {
+                            Name = g.Key,
+                            Count = g.Select(a => a.Key).Distinct().Count(),
+                            Versions = g.Select(a => a.Key).Distinct(),
+                            LibraryUniqueInstances = g.Select(a => a.Value).Distinct() //IEnumerable cause there can be several lib blocks with different rules in one version json
+                        };
+                    });
+
+                foreach (var lib in libraries)
+                {
+                    var subLibTreeNode = new TreeNode()
+                    {
+                        Title = lib.Name + " (" + lib.Count + ")",
+                    };
+
+                    //if only one version uses it (selected version), then add path to lib in Tag for deletion
+                    if (lib.Count == 1)
+                    {
+                        subLibTreeNode.Tag = lib.LibraryUniqueInstances.Where(l => l.Downloads?.Artifact?.Path is not null).Select(l => l.Downloads?.Artifact?.Path).FirstOrDefault() ?? string.Empty;
+                        //if fabric library
+                        if (File.Exists(Settings.minecraftForlderPath + "libraries/" + StartCommandBuilder.GetLibPathFromName(lib.Name)))
+                            subLibTreeNode.Tag = StartCommandBuilder.GetLibPathFromName(lib.Name);
+                    }
+
+                    var subLibUsedVersionsTreeNodeHeader = new TreeNode() { Title = "Using in versions:" };
+
+                    foreach (var ver in lib.Versions)
+                        subLibUsedVersionsTreeNodeHeader.SubNode.Add(new TreeNode() { Title = ver });
+
+                    if (subLibUsedVersionsTreeNodeHeader.SubNode.Count > 0)
+                        subLibTreeNode.SubNode.Add(subLibUsedVersionsTreeNodeHeader);
+
+                    if (lib.LibraryUniqueInstances.Any(l => l.Natives is not null) || lib.LibraryUniqueInstances.Any(l => l.Downloads?.Classifiers is not null))
+                    {
+                        var subLibNativesTreeNodeHeader = new TreeNode() { Title = "Natives:" };
+
+                        //check classifiers
+                        var libraryNativeInstance = lib.LibraryUniqueInstances.Where(l => l.Natives is not null || l.Downloads?.Classifiers is not null).First();
+
+                        if (libraryNativeInstance.Downloads?.Classifiers?.NativesWindows is not null)
+                        {
+                            subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode()
+                            {
+                                Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesWindows.Path) ?? "No lib name",
+                                Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesWindows?.Path ?? string.Empty : string.Empty
+                            });
+                        }
+
+                        if (libraryNativeInstance.Downloads?.Classifiers?.NativesWindows32 is not null)
+                        {
+                            subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode()
+                            {
+                                Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesWindows32.Path) ?? "No lib name",
+                                Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesWindows32?.Path ?? string.Empty : string.Empty
+                            });
+                        }
+
+                        if (libraryNativeInstance.Downloads?.Classifiers?.NativesWindows64 is not null)
+                        {
+                            subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode()
+                            {
+                                Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesWindows64.Path) ?? "No lib name",
+                                Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesWindows64?.Path ?? string.Empty : string.Empty
+                            });
+                        }
+
+                        if (libraryNativeInstance.Downloads?.Classifiers?.NativesLinux is not null)
+                        {
+                            subLibNativesTreeNodeHeader.SubNode.Add(new TreeNode()
+                            {
+                                Title = Path.GetFileName(libraryNativeInstance.Downloads?.Classifiers?.NativesLinux.Path) ?? "No lib name",
+                                Tag = lib.Count == 1 ? libraryNativeInstance.Downloads?.Classifiers?.NativesLinux?.Path ?? string.Empty : string.Empty
+                            });
+                        }
+
+                        if (subLibNativesTreeNodeHeader.SubNode.Count > 0)
+                            subLibTreeNode.SubNode.Add(subLibNativesTreeNodeHeader);
+
+                    }
+
+                    libTreeNode.SubNode.Add(subLibTreeNode);
+                }
+
+                _libraries = libTreeNode;
+                DownloadedVersionTree.Add(libTreeNode);
+            }
+            finally
+            {
+                this.RaisePropertyChanged(nameof(DownloadedVersionTree));
+                IsControlsEnabled = true;
+            }
+        });
+    }
+
     public async Task OnDeleteButtonClick()
     {
         await Task.Run(() => {
@@ -1007,6 +1031,12 @@ public class VersionsDownloaderViewModel : ViewModelBase
                 yield return new KeyValuePair<string, Library>(version.Key,lib);
         }
     }
+
+    public void Dispose()
+    {
+        this.PropertyChanged -= OnFilteredVersionPropertyChanged;
+        this.PropertyChanged -= OnDownloadedVersionPropertyChanged;
+    }
 }
 
 public class TreeNode