From 31387a9a6b18f621d18d43a25142034117b582cd Mon Sep 17 00:00:00 2001 From: a <81605232+Nirv-git@users.noreply.github.com> Date: Sun, 16 Apr 2023 15:09:45 -0500 Subject: [PATCH 1/4] Added Flags Added support for flags for mods, the idea behind this is to better call out mods that may be recommended or ones that may be niche but in a normal category --- CVRMelonAssistant/Classes/Mod.cs | 1 + CVRMelonAssistant/Localisation/en.xaml | 1 + CVRMelonAssistant/Pages/Mods.xaml | 28 +++++++++++++++++- CVRMelonAssistant/Pages/Mods.xaml.cs | 30 ++++++++++++++++++++ CVRMelonAssistant/Properties/AssemblyInfo.cs | 4 +-- 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/CVRMelonAssistant/Classes/Mod.cs b/CVRMelonAssistant/Classes/Mod.cs index ed0ce2d..0dd4b23 100644 --- a/CVRMelonAssistant/Classes/Mod.cs +++ b/CVRMelonAssistant/Classes/Mod.cs @@ -32,6 +32,7 @@ public class ModVersion public string ChilloutVRVersion; public string loaderVersion; public int approvalStatus; + public int flag; public bool IsBroken => approvalStatus == 2; public bool IsRetired => approvalStatus == 3; diff --git a/CVRMelonAssistant/Localisation/en.xaml b/CVRMelonAssistant/Localisation/en.xaml index 88436f1..83baeeb 100644 --- a/CVRMelonAssistant/Localisation/en.xaml +++ b/CVRMelonAssistant/Localisation/en.xaml @@ -113,6 +113,7 @@ Failed to extract {0}, trying again in {1} seconds. ({2}/{3}) Failed to extract {0} after max attempts ({1}), skipping. This mod might not work properly so proceed at your own risk Search... + ★ - Recommend for most users ♥ - Well liked !! - Niche, make sure you understand what the mod does About diff --git a/CVRMelonAssistant/Pages/Mods.xaml b/CVRMelonAssistant/Pages/Mods.xaml index ed3e2ae..1ccf464 100644 --- a/CVRMelonAssistant/Pages/Mods.xaml +++ b/CVRMelonAssistant/Pages/Mods.xaml @@ -51,7 +51,7 @@ Grid.Column="0" SelectionChanged="ModsListView_SelectionChanged" SelectionMode="Single" - MouseDoubleClick="ModsListView_OnMouseDoubleClick"> + MouseDoubleClick="ModsListView_OnMouseDoubleClick" Margin="0,0,0,20"> @@ -79,6 +79,21 @@ + + + + + + + + + @@ -159,6 +174,17 @@ + + + diff --git a/CVRMelonAssistant/Pages/Mods.xaml.cs b/CVRMelonAssistant/Pages/Mods.xaml.cs index 1003790..c427b7a 100644 --- a/CVRMelonAssistant/Pages/Mods.xaml.cs +++ b/CVRMelonAssistant/Pages/Mods.xaml.cs @@ -295,6 +295,7 @@ ModListItem.CategoryInfo GetCategory(Mod mod) { IsSelected = preSelected, IsEnabled = true, + Flag = latestVersion.flag, ModName = latestVersion.name, ModVersion = latestVersion.modVersion, ModAuthor = HardcodedCategories.FixupAuthor(latestVersion.author), @@ -373,6 +374,7 @@ public class ModListItem public bool IsEnabled { get; set; } public bool IsSelected { get; set; } + public int Flag { get; set; } public Mod ModInfo { get; set; } public CategoryInfo Category { get; set; } @@ -399,6 +401,34 @@ public string InstalledVersion } } + public string GetFlagString + { + get + { + switch (Flag) + { + case 1: return "★"; + case 2: return "♥"; + case 3: return "!!"; + default: return ""; + } + } + } + + public string GetFlagColor + { + get + { + switch(Flag) + { + case 1: return "#FFD700"; + case 2: return "#98002e"; + case 3: return "#FF0000"; + default: return ""; + } + } + } + public string GetVersionColor { get diff --git a/CVRMelonAssistant/Properties/AssemblyInfo.cs b/CVRMelonAssistant/Properties/AssemblyInfo.cs index 6e34e6c..bf77917 100644 --- a/CVRMelonAssistant/Properties/AssemblyInfo.cs +++ b/CVRMelonAssistant/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.28.1029")] -[assembly: AssemblyFileVersion("1.1.28.1029+cvr")] +[assembly: AssemblyVersion("1.1.29.1029")] +[assembly: AssemblyFileVersion("1.1.29.1029+cvr")] From 997105eebc78b9059f7b607a3ec59f0482c88bcd Mon Sep 17 00:00:00 2001 From: a <81605232+Nirv-git@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:26:13 -0500 Subject: [PATCH 2/4] ML, ABI to CVR, Flags Changed ML install to 0.7.2 nightly instead of Latest Changed ABI references to CVR Added Flags to Mods to show commonly recommend ones Update URL changed to Nirv-git --- CVRMelonAssistant/Classes/InstallHandlers.cs | 2 +- CVRMelonAssistant/Classes/Mod.cs | 7 +++- CVRMelonAssistant/Classes/Updater.cs | 2 +- CVRMelonAssistant/Classes/Utils.cs | 1 + CVRMelonAssistant/Localisation/en.xaml | 5 +-- CVRMelonAssistant/Pages/Mods.xaml.cs | 38 +++++++++++++++++--- CVRMelonAssistant/Pages/Options.xaml.cs | 2 +- CVRMelonAssistant/Properties/AssemblyInfo.cs | 4 +-- 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/CVRMelonAssistant/Classes/InstallHandlers.cs b/CVRMelonAssistant/Classes/InstallHandlers.cs index 859a6f8..0db8de4 100644 --- a/CVRMelonAssistant/Classes/InstallHandlers.cs +++ b/CVRMelonAssistant/Classes/InstallHandlers.cs @@ -49,7 +49,7 @@ public static async Task InstallMelonLoader() { MainWindow.Instance.MainText = $"{(string) App.Current.FindResource("Mods:DownloadingMelonLoader")}..."; - using var installerZip = await DownloadFileToMemory("https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x64.zip"); + using var installerZip = await DownloadFileToMemory("https://nightly.link/LavaGang/MelonLoader/actions/runs/17447824590/MelonLoader.Windows.x64.CI.Release.zip"); using var zipReader = new ZipArchive(installerZip, ZipArchiveMode.Read); MainWindow.Instance.MainText = $"{(string) App.Current.FindResource("Mods:UnpackingMelonLoader")}..."; diff --git a/CVRMelonAssistant/Classes/Mod.cs b/CVRMelonAssistant/Classes/Mod.cs index 0dd4b23..86d85f7 100644 --- a/CVRMelonAssistant/Classes/Mod.cs +++ b/CVRMelonAssistant/Classes/Mod.cs @@ -16,6 +16,7 @@ public class Mod public string installedVersion; public bool installedInBrokenDir; public bool installedInRetiredDir; + public int flag; public class ModVersion { @@ -32,11 +33,15 @@ public class ModVersion public string ChilloutVRVersion; public string loaderVersion; public int approvalStatus; - public int flag; public bool IsBroken => approvalStatus == 2; public bool IsRetired => approvalStatus == 3; public bool IsPlugin => modType.Equals("plugin", StringComparison.InvariantCultureIgnoreCase); } } + public class FlagEntry + { + public int _id; + public int flag; + } } diff --git a/CVRMelonAssistant/Classes/Updater.cs b/CVRMelonAssistant/Classes/Updater.cs index ddeea9f..5bc3c3f 100644 --- a/CVRMelonAssistant/Classes/Updater.cs +++ b/CVRMelonAssistant/Classes/Updater.cs @@ -9,7 +9,7 @@ namespace CVRMelonAssistant { class Updater { - private static readonly string APILatestURL = "https://api.github.com/repos/knah/CVRMelonAssistant/releases/latest"; + private static readonly string APILatestURL = "https://api.github.com/repos/Nirv-git/CVRMelonAssistant/releases/latest"; private static Update LatestUpdate; private static Version CurrentVersion; diff --git a/CVRMelonAssistant/Classes/Utils.cs b/CVRMelonAssistant/Classes/Utils.cs index e4abaec..d765da7 100644 --- a/CVRMelonAssistant/Classes/Utils.cs +++ b/CVRMelonAssistant/Classes/Utils.cs @@ -26,6 +26,7 @@ public class Constants { public const string ChilloutVRAppId = "661130"; public const string CVRMGModsJson = "https://api.cvrmg.com/v1/mods"; + public const string CVRMGModsFlagsJson = "https://gist.githubusercontent.com/Nirv-git/1963e20d855c401349820a93b4d2639b/raw/cvrModFlags.json"; public const string WeebCDNAPIURL = "https://pat.assistant.moe/api/v1.0/"; public const string MD5Spacer = " "; public static readonly char[] IllegalCharacters = new char[] diff --git a/CVRMelonAssistant/Localisation/en.xaml b/CVRMelonAssistant/Localisation/en.xaml index 83baeeb..bf32224 100644 --- a/CVRMelonAssistant/Localisation/en.xaml +++ b/CVRMelonAssistant/Localisation/en.xaml @@ -38,7 +38,7 @@ ChilloutVR allows modding the game under certain conditions, specifically as long as mods are not used for evil. - ChilloutVR Modding Assistant and mods provided here are not associated with or endorsed by Alpha Blend Interactive (the devloper). + ChilloutVR Modding Assistant and mods provided here are not associated with or endorsed by ChilloutVR. Mods @@ -113,7 +113,8 @@ Failed to extract {0}, trying again in {1} seconds. ({2}/{3}) Failed to extract {0} after max attempts ({1}), skipping. This mod might not work properly so proceed at your own risk Search... - ★ - Recommend for most users ♥ - Well liked !! - Niche, make sure you understand what the mod does + ★ - Recommend for most users ⓘ - Niche, make sure you understand what the mod does + About diff --git a/CVRMelonAssistant/Pages/Mods.xaml.cs b/CVRMelonAssistant/Pages/Mods.xaml.cs index c427b7a..8bfcf48 100644 --- a/CVRMelonAssistant/Pages/Mods.xaml.cs +++ b/CVRMelonAssistant/Pages/Mods.xaml.cs @@ -21,6 +21,7 @@ using MessageBox = System.Windows.MessageBox; using TextBox = System.Windows.Controls.TextBox; + namespace CVRMelonAssistant.Pages { /// @@ -137,6 +138,8 @@ public async Task CheckInstalledMods() { await GetAllMods(); + await GetFlagMapping(); + await Task.Run(() => { CheckInstallDir("Plugins"); @@ -171,6 +174,31 @@ public async Task GetAllMods() } } + public async Task GetFlagMapping() + { + try + { + var resp = await HttpClient.GetAsync(Utils.Constants.CVRMGModsFlagsJson); + var body = await resp.Content.ReadAsStringAsync(); + + var entries = JsonSerializer.Deserialize(body); + + if (entries == null || entries.Length == 0 || AllModsList == null) return; + + var flagById = new Dictionary(entries.Length); + foreach (var e in entries) + flagById[e._id] = e.flag; + + foreach (var mod in AllModsList) + mod.flag = flagById.TryGetValue(mod._id, out var f) ? f : 0; + + } + catch (Exception e) + { + System.Windows.MessageBox.Show($"{FindResource("Mods:LoadFailed")} - Flags.\n\n" + e); + } + } + private void CheckInstallDir(string directory, bool isBrokenDir = false, bool isRetiredDir = false) { if (!Directory.Exists(Path.Combine(App.ChilloutInstallDirectory, directory))) @@ -180,8 +208,8 @@ private void CheckInstallDir(string directory, bool isBrokenDir = false, bool is foreach (string file in Directory.GetFileSystemEntries(Path.Combine(App.ChilloutInstallDirectory, directory), "*.dll", SearchOption.TopDirectoryOnly)) { - if (!File.Exists(file) || Path.GetExtension(file) != ".dll") continue; + if (!File.Exists(file) || Path.GetExtension(file) != ".dll") continue; var modInfo = ExtractModVersions(file); if (modInfo.Item1 != null && modInfo.Item2 != null) { @@ -295,7 +323,7 @@ ModListItem.CategoryInfo GetCategory(Mod mod) { IsSelected = preSelected, IsEnabled = true, - Flag = latestVersion.flag, + Flag = mod.flag, ModName = latestVersion.name, ModVersion = latestVersion.modVersion, ModAuthor = HardcodedCategories.FixupAuthor(latestVersion.author), @@ -409,7 +437,7 @@ public string GetFlagString { case 1: return "★"; case 2: return "♥"; - case 3: return "!!"; + case 3: return "ⓘ"; //!! ⓘ Ⓘ default: return ""; } } @@ -421,9 +449,9 @@ public string GetFlagColor { switch(Flag) { - case 1: return "#FFD700"; + case 1: return "#d6b600"; //FFD700 case 2: return "#98002e"; - case 3: return "#FF0000"; + case 3: return "#e06c00"; //orange #e06c00 purpl #7000e0 default: return ""; } } diff --git a/CVRMelonAssistant/Pages/Options.xaml.cs b/CVRMelonAssistant/Pages/Options.xaml.cs index 3f45623..446fec7 100644 --- a/CVRMelonAssistant/Pages/Options.xaml.cs +++ b/CVRMelonAssistant/Pages/Options.xaml.cs @@ -67,7 +67,7 @@ private void OpenAppDataButton_Click(object sender, RoutedEventArgs e) { string location = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - "AppData", "LocalLow", "Alpha Blend Interactive", "ChilloutVR"); + "AppData", "LocalLow", "ChilloutVR", "ChilloutVR"); if (Directory.Exists(location)) { Utils.OpenFolder(location); diff --git a/CVRMelonAssistant/Properties/AssemblyInfo.cs b/CVRMelonAssistant/Properties/AssemblyInfo.cs index bf77917..e378693 100644 --- a/CVRMelonAssistant/Properties/AssemblyInfo.cs +++ b/CVRMelonAssistant/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.29.1029")] -[assembly: AssemblyFileVersion("1.1.29.1029+cvr")] +[assembly: AssemblyVersion("1.1.35.1029")] +[assembly: AssemblyFileVersion("1.1.35.1029+cvr")] From ab5eb37487282a67b3ac1b5361c8ed752a209320 Mon Sep 17 00:00:00 2001 From: a <81605232+Nirv-git@users.noreply.github.com> Date: Fri, 19 Sep 2025 13:36:23 -0500 Subject: [PATCH 3/4] Lag and Formatting Changes Lag reduction on Mods list, Search only updates when the search text is at least 3 characters or is blanked out Forced Virtualizing and static Column widths to speed up Mod page generation Basic Markdown and Link processing to Mod Description --- CVRMelonAssistant/Localisation/en.xaml | 2 +- CVRMelonAssistant/ModInfoWindow.xaml | 2 +- CVRMelonAssistant/ModInfoWindow.xaml.cs | 96 +++++++++++++++++++- CVRMelonAssistant/Pages/Mods.xaml | 35 ++++--- CVRMelonAssistant/Pages/Mods.xaml.cs | 7 +- CVRMelonAssistant/Properties/AssemblyInfo.cs | 4 +- 6 files changed, 127 insertions(+), 19 deletions(-) diff --git a/CVRMelonAssistant/Localisation/en.xaml b/CVRMelonAssistant/Localisation/en.xaml index bf32224..4c150a5 100644 --- a/CVRMelonAssistant/Localisation/en.xaml +++ b/CVRMelonAssistant/Localisation/en.xaml @@ -201,5 +201,5 @@ No description available Download link: Source code: - Internal ID: {0} {1} + Internal ID: {0} - {1} diff --git a/CVRMelonAssistant/ModInfoWindow.xaml b/CVRMelonAssistant/ModInfoWindow.xaml index 307bd32..3736d67 100644 --- a/CVRMelonAssistant/ModInfoWindow.xaml +++ b/CVRMelonAssistant/ModInfoWindow.xaml @@ -18,7 +18,7 @@ - + diff --git a/CVRMelonAssistant/ModInfoWindow.xaml.cs b/CVRMelonAssistant/ModInfoWindow.xaml.cs index 7df44b1..47a1dfb 100644 --- a/CVRMelonAssistant/ModInfoWindow.xaml.cs +++ b/CVRMelonAssistant/ModInfoWindow.xaml.cs @@ -1,6 +1,9 @@ using System; +using System.Text.RegularExpressions; using System.Windows; +using System.Windows.Controls; using System.Windows.Documents; +using static System.Windows.Forms.LinkLabel; namespace CVRMelonAssistant { @@ -16,6 +19,10 @@ public void SetMod(Mod mod) Title = string.Format((string) FindResource("ModInfoWindow:Title"), mod.versions[0].name); ModDescription.Text = mod.versions[0].description ?? (string) FindResource("ModInfoWindow:NoDescription"); + + var desc = mod.versions[0].description ?? (string)FindResource("ModInfoWindow:NoDescription"); + SetMarkdownText(ModDescription, desc); + ModName.Text = mod.versions[0].name; ModAuthor.Text = string.Format((string) FindResource("ModInfoWindow:Author"), mod.versions[0].author ?? FindResource("ModInfoWindow:NoAuthor")); ModVersion.Text = mod.versions[0].modVersion; @@ -39,9 +46,96 @@ public void SetMod(Mod mod) InternalIds.Text = string.Format((string) FindResource("ModInfoWindow:InternalIds"), mod._id, mod.versions[0]._version); } + private static readonly Regex Linkish = new( + @"\[(?[^\]]+)\]\((?https?://[^\s)]+)\)|<(?https?://[^>\s]+)>", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + private static readonly Regex Emphasis = new( + @"(?\*\*\*.+?\*\*\*|___.+?___)|(?\*\*.+?\*\*|__.+?__)|(?\*(?!\s).+?\*|_(?!\s).+?_)", + RegexOptions.Compiled); + + public void SetMarkdownText(TextBlock tb, string? text) + { + tb.Inlines.Clear(); + if (string.IsNullOrEmpty(text)) return; + + int last = 0; + foreach (Match m in Linkish.Matches(text)) + { + if (m.Index > last) + AddFormattedText(tb.Inlines, text.Substring(last, m.Index - last)); + + if (m.Groups["url"].Success) + { // [text](url) + var url = m.Groups["url"].Value; + var linkText = m.Groups["text"].Value; + var link = CreateHyperlink(linkText, url); + tb.Inlines.Add(link); + } + else + { // + string url = m.Groups["auto"].Value; + tb.Inlines.Add(CreateHyperlink(url)); + } + last = m.Index + m.Length; + } + + if (last < text.Length) + AddFormattedText(tb.Inlines, text.Substring(last)); + } + + // Parse **bold**, *italics*, and ***bold+italics*** + private static void AddFormattedText(InlineCollection inlines, string text) + { + int last = 0; + foreach (Match m in Emphasis.Matches(text)) + { + if (m.Index > last) + inlines.Add(new Run(text.Substring(last, m.Index - last))); + + Inline styled; + if (m.Groups["strongem"].Success) + { + string inner = StripOuter(m.Value, 3); + styled = new Run(inner) { FontWeight = FontWeights.Bold, FontStyle = FontStyles.Italic }; + } + else if (m.Groups["strong"].Success) + { + string inner = StripOuter(m.Value, 2); + styled = new Run(inner) { FontWeight = FontWeights.Bold }; + } + else // em + { + string inner = StripOuter(m.Value, 1); + styled = new Run(inner) { FontStyle = FontStyles.Italic }; + } + + inlines.Add(styled); + last = m.Index + m.Length; + } + + if (last < text.Length) + inlines.Add(new Run(text.Substring(last))); + } + + private static string StripOuter(string s, int n) + { + if (s.Length >= 2 * n) return s.Substring(n, s.Length - 2 * n); + return s; + } + private static Hyperlink CreateHyperlink(string uri) { - var link = new Hyperlink(new Run(uri)) {NavigateUri = new Uri(uri)}; + var link = new Hyperlink { NavigateUri = new Uri(uri) }; + link.Inlines.Add(new Run(uri)); + link.RequestNavigate += HyperlinkExtensions.Hyperlink_RequestNavigate; + return link; + } + + private static Hyperlink CreateHyperlink(string text, string uri) + { + var link = new Hyperlink { NavigateUri = new Uri(uri) }; + link.Inlines.Add(new Run(text)); link.RequestNavigate += HyperlinkExtensions.Hyperlink_RequestNavigate; return link; } diff --git a/CVRMelonAssistant/Pages/Mods.xaml b/CVRMelonAssistant/Pages/Mods.xaml index 1ccf464..d2890a7 100644 --- a/CVRMelonAssistant/Pages/Mods.xaml +++ b/CVRMelonAssistant/Pages/Mods.xaml @@ -28,34 +28,45 @@ + Text="{DynamicResource Mods:SearchLabel}" + FontSize="20"/> + TextChanged="SearchBar_TextChanged" + FontSize="20"/> + MouseDoubleClick="ModsListView_OnMouseDoubleClick" + Margin="0,0,0,20" + ScrollViewer.CanContentScroll="True" + VirtualizingStackPanel.IsVirtualizing="True" + VirtualizingStackPanel.VirtualizationMode="Recycling" + VirtualizingPanel.IsVirtualizingWhenGrouping="True" + VirtualizingPanel.ScrollUnit="Pixel" + VirtualizingPanel.CacheLengthUnit="Item" + VirtualizingPanel.CacheLength="2"> + - +