diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml new file mode 100644 index 0000000..852c2d6 --- /dev/null +++ b/.github/workflows/github-release.yml @@ -0,0 +1,67 @@ +name: GitHub Release + +on: + push: + tags: + - 'v*' # Push events to matching v*, i.e. v20.15.10 + +jobs: + tagged-release: + name: "Tagged Release" + runs-on: "windows-latest" + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Dotnet Setup + uses: actions/setup-dotnet@v1 + + - name: Publish Windows x64 + run: dotnet publish -c Release --sc -r win-x64 --output ./BepInEx_GUI_win_x64_${{github.ref_name}} + + - name: Zip Windows x64 Release + uses: papeloto/action-zip@v1 + with: + files: BepInEx_GUI_win_x64_${{github.ref_name}}/ + recursive: false + dest: BepInEx_GUI_win_x64_${{github.ref_name}}.zip + + - name: Publish Windows x86 + run: dotnet publish -c Release --sc -r win-x86 --output ./BepInEx_GUI_win_x86_${{github.ref_name}} + + - name: Zip Windows x86 Release + uses: papeloto/action-zip@v1 + with: + files: BepInEx_GUI_win_x86_${{github.ref_name}}/ + recursive: false + dest: BepInEx_GUI_win_x86_${{github.ref_name}}.zip + + - name: Publish Linux x64 + run: dotnet publish -c Release --sc -r linux-x64 --output ./BepInEx_GUI_linux_x64_${{github.ref_name}} + + - name: Zip Linux x64 Release + uses: papeloto/action-zip@v1 + with: + files: BepInEx_GUI_linux_x64_${{github.ref_name}}/ + recursive: false + dest: BepInEx_GUI_linux_x64_${{github.ref_name}}.zip + + - name: Publish macOS x64 + run: dotnet publish -c Release --sc -r osx-x64 --output ./BepInEx_GUI_macOS_x64_${{github.ref_name}} + + - name: Zip macOS x64 Release + uses: papeloto/action-zip@v1 + with: + files: BepInEx_GUI_macOS_x64_${{github.ref_name}}/ + recursive: false + dest: BepInEx_GUI_macOS_x64_${{github.ref_name}}.zip + + - name: "Make GitHub Release" + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: false + files: | + *.zip \ No newline at end of file diff --git a/BepInEx.GUI.Config/MainConfig.cs b/BepInEx.GUI.Config/MainConfig.cs index 61b7074..5b3d69b 100644 --- a/BepInEx.GUI.Config/MainConfig.cs +++ b/BepInEx.GUI.Config/MainConfig.cs @@ -1,4 +1,6 @@ -using BepInEx.Configuration; +using BepInEx.Configuration; +using System; +using System.IO; namespace BepInEx.GUI.Config { @@ -13,6 +15,46 @@ public static class MainConfig public const string EnableDeveloperToolsText = "Enable Developer Tools"; public static ConfigEntry EnableDeveloperToolsConfig { get; private set; } + /// + /// This is done through LocalApplicationData because + /// the BepInEx.GUI cfg file may be copied across different users + /// by r2modman profile sharing feature + /// thus they may never end up seeing the one time only disclaimer + /// + public static bool ShowOneTimeOnlyDisclaimerConfig + { + get + { + try + { + var localAppDataFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (string.IsNullOrWhiteSpace(localAppDataFolderPath)) + { + return false; + } + + var bepinexGuiAppDataFolderPath = Path.Combine(localAppDataFolderPath, "BepInEx.GUI"); + + var alreadyShownDisclaimer = Directory.Exists(bepinexGuiAppDataFolderPath); + if (alreadyShownDisclaimer) + { + return false; + + } + else + { + Directory.CreateDirectory(bepinexGuiAppDataFolderPath); + return true; + } + } + catch (Exception) + { + } + + return false; + } + } + public const string CloseWindowWhenGameLoadedConfigKey = "Close Window When Game Loaded"; public const string CloseWindowWhenGameLoadedConfigDescription = "Close the graphic user interface window when the game is loaded"; public static ConfigEntry CloseWindowWhenGameLoadedConfig { get; private set; } diff --git a/BepInEx.GUI.Patcher/BepInEx.GUI.Patcher.csproj b/BepInEx.GUI.Patcher/BepInEx.GUI.Patcher.csproj index 7174625..cd5f9e6 100644 --- a/BepInEx.GUI.Patcher/BepInEx.GUI.Patcher.csproj +++ b/BepInEx.GUI.Patcher/BepInEx.GUI.Patcher.csproj @@ -6,7 +6,7 @@ - + diff --git a/BepInEx.GUI.Patcher/CloseGuiOnChainloaderDone.cs b/BepInEx.GUI.Patcher/CloseGuiOnChainloaderDone.cs index 2a8ba8f..073c326 100644 --- a/BepInEx.GUI.Patcher/CloseGuiOnChainloaderDone.cs +++ b/BepInEx.GUI.Patcher/CloseGuiOnChainloaderDone.cs @@ -23,7 +23,6 @@ public void LogEvent(object sender, LogEventArgs eventArgs) if (eventArgs.Data.ToString() == "Chainloader startup complete" && eventArgs.Level.Equals(LogLevel.Message)) { - MainConfig.Init(Path.Combine(Paths.ConfigPath, MainConfig.FileName)); if (MainConfig.CloseWindowWhenGameLoadedConfig.Value) { diff --git a/BepInEx.GUI.Patcher/Patcher.cs b/BepInEx.GUI.Patcher/Patcher.cs index a805a44..7bef2e4 100644 --- a/BepInEx.GUI.Patcher/Patcher.cs +++ b/BepInEx.GUI.Patcher/Patcher.cs @@ -1,4 +1,4 @@ -using BepInEx.Configuration; +using BepInEx.Configuration; using BepInEx.Logging; using Mono.Cecil; using MonoMod.Utils; @@ -10,6 +10,7 @@ namespace BepInEx.GUI.Patcher { + [BepInDependency("PassivePicasso.WebSlog.WebSocketLogServer", BepInDependency.DependencyFlags.HardDependency)] public static class Patcher { public static IEnumerable TargetDLLs => Enumerable.Empty(); @@ -31,16 +32,23 @@ public static void Initialize() } else { - string executablePath = FindGuiExecutable(); - if (executablePath != null) - { - LaunchGui(executablePath); - } - else - { - LogSource.LogMessage("BepInEx.GUI executable not found."); - LogSource.Dispose(); - } + FindAndLaunchGui(); + } + } + + private static void FindAndLaunchGui() + { + Patcher.LogSource.LogMessage("Finding and launching GUI"); + + string executablePath = FindGuiExecutable(); + if (executablePath != null) + { + LaunchGui(executablePath); + } + else + { + LogSource.LogMessage("BepInEx.GUI executable not found."); + LogSource.Dispose(); } } @@ -52,20 +60,34 @@ private static string FindGuiExecutable() const string GuiFileName = "BepInEx.GUI"; + const Platform windowsPlatform = Platform.Windows; const Platform windowsX64Platform = Platform.Windows | Platform.Bits64; + const Platform linuxX64Platform = Platform.Linux | Platform.Bits64; + const Platform macOsX64Platform = Platform.MacOS | Platform.Bits64; var platform = PlatformHelper.Current; - var isWindows = (platform & windowsX64Platform) == platform; - var isLinux = (platform & linuxX64Platform) == platform; - var isMacOs = (platform & macOsX64Platform) == platform; + var isWindows = (platform & windowsPlatform) == platform; + var isWindows64 = (platform & windowsX64Platform) == platform; + + // linux x86 https://github.com/dotnet/runtime/issues/31180 + var isLinux64 = (platform & linuxX64Platform) == platform; + + var isMacOs64 = (platform & macOsX64Platform) == platform; + + var filePathLower = filePath.ToLowerInvariant(); // Not the best but should work... - if ((isWindows && fileName == $"{GuiFileName}.exe") || - (isLinux && fileName == GuiFileName && filePath.ToLowerInvariant().Contains("linux")) || - (isMacOs && fileName == GuiFileName && filePath.ToLowerInvariant().Contains("osx"))) + if ( + (isWindows && fileName == $"{GuiFileName}.exe" && filePathLower.Contains("86")) || + (isWindows64 && fileName == $"{GuiFileName}.exe" && filePathLower.Contains("64")) || + + (isLinux64 && fileName == GuiFileName && filePathLower.Contains("linux64")) || + + (isMacOs64 && fileName == GuiFileName && filePathLower.Contains("macos64")) + ) { return filePath; } diff --git a/BepInEx.GUI/App.axaml b/BepInEx.GUI/App.axaml index 99c6ea0..ad9a1c7 100644 --- a/BepInEx.GUI/App.axaml +++ b/BepInEx.GUI/App.axaml @@ -2,11 +2,51 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:BepInEx.GUI" x:Class="BepInEx.GUI.App"> + - + + + + + + + + + + + + + + + + + + + + diff --git a/BepInEx.GUI/App.axaml.cs b/BepInEx.GUI/App.axaml.cs index bdb5b3d..2c177cd 100644 --- a/BepInEx.GUI/App.axaml.cs +++ b/BepInEx.GUI/App.axaml.cs @@ -1,15 +1,19 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using BepInEx.GUI.Config; using BepInEx.GUI.Models; using BepInEx.GUI.ViewModels; using BepInEx.GUI.Views; +using System; using WebSocketSharp; namespace BepInEx.GUI { public class App : Application { + private WebSocket? _webSocket; + public override void Initialize() { AvaloniaXamlLoader.Load(this); @@ -21,24 +25,28 @@ public override void OnFrameworkInitializationCompleted() { desktop.Startup += (sender, eventArgs) => { + AppDomain.CurrentDomain.UnhandledException += ShowUnhandledException; + var args = DefaultArgsIfNoneProvided(eventArgs.Args); var platformInfo = new PlatformInfo(args); var pathsInfo = new PathsInfo(args); var targetInfo = new TargetInfo(args); - var webSocket = new WebSocket("ws://localhost:5892/Log"); - webSocket.Connect(); + _webSocket = new WebSocket("ws://localhost:5892/Log"); + _webSocket.Connect(); + + MainConfig.Init(pathsInfo.ConfigFilePath); desktop.MainWindow = new MainWindow { - DataContext = new MainWindowViewModel(webSocket, pathsInfo, platformInfo, targetInfo), + DataContext = new MainWindowViewModel(_webSocket, pathsInfo, platformInfo, targetInfo), }; // Needed or the target closes desktop.MainWindow.Closing += (sender, e) => { - webSocket.Close(); + _webSocket.Close(); }; }; } @@ -46,6 +54,16 @@ public override void OnFrameworkInitializationCompleted() base.OnFrameworkInitializationCompleted(); } + private void ShowUnhandledException(object sender, UnhandledExceptionEventArgs e) + { + // Avalonia could possibly not be able to recover at all from this exception, thus closing the app + // the websocket if left open at this point will crash the target + _webSocket!.Close(); + + var ex = (Exception)e.ExceptionObject; + Debug.Message(ex.ToString()); + } + private static string[] DefaultArgsIfNoneProvided(string[] args) { if (args.Length == 0) diff --git a/BepInEx.GUI/BepInEx.GUI.csproj b/BepInEx.GUI/BepInEx.GUI.csproj index 8ce5333..48c01c3 100644 --- a/BepInEx.GUI/BepInEx.GUI.csproj +++ b/BepInEx.GUI/BepInEx.GUI.csproj @@ -14,10 +14,14 @@ - - + + + + ..\libs\protobuf-net.dll + + diff --git a/BepInEx.GUI/Models/LogEntry.cs b/BepInEx.GUI/Models/LogEntry.cs index 8fa8848..1177962 100644 --- a/BepInEx.GUI/Models/LogEntry.cs +++ b/BepInEx.GUI/Models/LogEntry.cs @@ -36,7 +36,7 @@ public class LogEntry } catch (Exception e) { - // todo + Debug.Message($"Error deserializing log entry: {e}"); } return null; diff --git a/BepInEx.GUI/Models/TargetInfo.cs b/BepInEx.GUI/Models/TargetInfo.cs index 1bcc2ff..9b01dd4 100644 --- a/BepInEx.GUI/Models/TargetInfo.cs +++ b/BepInEx.GUI/Models/TargetInfo.cs @@ -10,8 +10,10 @@ public class TargetInfo public TargetInfo(string[] args) { - if (int.TryParse(args[6], out var id)); + if (int.TryParse(args[6], out var id)) Id = id; + else + Debug.Message("Error parsing args[6] for target process id"); Process = Process.GetProcessById(Id); } diff --git a/BepInEx.GUI/Models/Thunderstore/Communities.cs b/BepInEx.GUI/Models/Thunderstore/Communities.cs new file mode 100644 index 0000000..fe69996 --- /dev/null +++ b/BepInEx.GUI/Models/Thunderstore/Communities.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace BepInEx.GUI.Models.Thunderstore +{ + public class Communities + { + [JsonPropertyName("pagination")] + public Pagination? Pagination { get; set; } + + [JsonPropertyName("results")] + public List? Results { get; set; } + } + + public class Pagination + { + [JsonPropertyName("next_link")] + public object? NextLink { get; set; } + + [JsonPropertyName("previous_link")] + public object? PreviousLink { get; set; } + } + + public class Result + { + [JsonPropertyName("identifier")] + public string? Identifier { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("discord_url")] + public Uri? DiscordUrl { get; set; } + + [JsonPropertyName("wiki_url")] + public Uri? WikiUrl { get; set; } + + [JsonPropertyName("require_package_listing_approval")] + public bool RequirePackageListingApproval { get; set; } + } +} diff --git a/BepInEx.GUI/ViewModels/ConsoleViewModel.cs b/BepInEx.GUI/ViewModels/ConsoleViewModel.cs index 6b3a0be..9630c25 100644 --- a/BepInEx.GUI/ViewModels/ConsoleViewModel.cs +++ b/BepInEx.GUI/ViewModels/ConsoleViewModel.cs @@ -1,20 +1,59 @@ using BepInEx.GUI.Models; +using BepInEx.GUI.Views; using ReactiveUI; using System; using System.Collections.Generic; -using System.Text; +using System.Collections.ObjectModel; using WebSocketSharp; namespace BepInEx.GUI.ViewModels { public class ConsoleViewModel : ViewModelBase { + public class ColoredEntry + { + public string Text { get; set; } + public string Color { get; set; } + + public ColoredEntry(string text, string color) + { + Text = text; + Color = color; + } + } + public WebSocket WebSocket { get; } public TargetInfo TargetInfo { get; } + public PlatformInfo PlatformInfo { get; } + public List LogEntries { get; } + private string _pauseButtonText = "Pause Game"; + public string PauseButtonText + { + get { return _pauseButtonText; } + set + { + this.RaiseAndSetIfChanged(ref _pauseButtonText, value); + } + } + + private bool _consoleAutoScroll = true; + public bool ConsoleAutoScroll + { + get { return _consoleAutoScroll; } + set + { + ConsoleView.ConsoleAutoScroll = value; + this.RaiseAndSetIfChanged(ref _consoleAutoScroll, value); + + if (ConsoleView.ConsoleAutoScroll) + ConsoleView.ScrollToEnd(); + } + } + private string _textFilter = ""; public string TextFilter { @@ -26,8 +65,8 @@ public string TextFilter } } - private string _consoleText = ""; - public string ConsoleText + private ObservableCollection _consoleText = new(); + public ObservableCollection ConsoleText { get { return _consoleText; } set @@ -46,17 +85,30 @@ public int LogFilterLevel { this.RaiseAndSetIfChanged(ref _logFilterLevel, value); _allowedLogLevel = _logLevels[_logFilterLevel]; + LogFilterLevelText = "Log Filter Level : " + _allowedLogLevel.ToString(); UpdateConsoleBox(); } } - public ConsoleViewModel(WebSocket webSocket, TargetInfo targetInfo) + private string _logFilterLevelText = "Log Filter Level : " + Logging.LogLevel.All; + public string LogFilterLevelText + { + get { return _logFilterLevelText; } + set + { + this.RaiseAndSetIfChanged(ref _logFilterLevelText, value); + } + } + + public ConsoleViewModel(WebSocket webSocket, TargetInfo targetInfo, PlatformInfo platformInfo) { WebSocket = webSocket; WebSocket.OnMessage += AddLogToConsole; TargetInfo = targetInfo; + PlatformInfo = platformInfo; + LogEntries = new(); } @@ -72,42 +124,59 @@ private void AddLogToConsole(object? sender, MessageEventArgs e) private void UpdateConsoleBox() { - var stringBuilder = new StringBuilder(); + var consoleText = new ObservableCollection(); foreach (var logEntry in LogEntries) { if (logEntry.LevelCode <= _allowedLogLevel) { + var logEntryString = logEntry.ToString(); + + string color = logEntry.LevelCode switch + { + Logging.LogLevel.Fatal => "Red", + Logging.LogLevel.Error => "Red", + Logging.LogLevel.Warning => "YellowGreen", + _ => "Transparent", + }; + if (TextFilter.Length > 0) { - var logEntryString = logEntry.ToString(); if (logEntryString.ToLowerInvariant().Contains(TextFilter.ToLowerInvariant())) { - stringBuilder.AppendLine(logEntryString); + consoleText.Add(new ColoredEntry(logEntryString, color)); } } else { - stringBuilder.AppendLine(logEntry.ToString()); + consoleText.Add(new ColoredEntry(logEntryString, color)); } } } - ConsoleText = stringBuilder.ToString(); + ConsoleText = consoleText; } private bool _isTargetPaused; public void OnClickPauseGame() { + if (!PlatformInfo.IsWindows) + { + Debug.Message("Windows only feature."); + return; + } + if (TargetInfo.Process != null && TargetInfo.Id != 0 && !TargetInfo.Process.HasExited) { if (_isTargetPaused) { TargetInfo.Process.Resume(); + PauseButtonText = "Pause Game"; } else { TargetInfo.Process.Suspend(); + PauseButtonText = "Resume Game"; } _isTargetPaused = !_isTargetPaused; diff --git a/BepInEx.GUI/ViewModels/DetailedLogEntryWindowViewModel.cs b/BepInEx.GUI/ViewModels/DetailedLogEntryWindowViewModel.cs new file mode 100644 index 0000000..e43516e --- /dev/null +++ b/BepInEx.GUI/ViewModels/DetailedLogEntryWindowViewModel.cs @@ -0,0 +1,12 @@ +namespace BepInEx.GUI.ViewModels +{ + public class DetailedLogEntryWindowViewModel : ViewModelBase + { + public string Text { get; } + + public DetailedLogEntryWindowViewModel(string text) + { + Text = text; + } + } +} diff --git a/BepInEx.GUI/ViewModels/DisclaimerWindowViewModel.cs b/BepInEx.GUI/ViewModels/DisclaimerWindowViewModel.cs new file mode 100644 index 0000000..2812aaf --- /dev/null +++ b/BepInEx.GUI/ViewModels/DisclaimerWindowViewModel.cs @@ -0,0 +1,22 @@ +using System; + +namespace BepInEx.GUI.ViewModels +{ + public class DisclaimerWindowViewModel : ViewModelBase + { + public string DisclaimerText { get; } = + "The console is now disabled by default." + Environment.NewLine + + "For correct troubleshooting, please read below." + Environment.NewLine + Environment.NewLine + + + "By not posting a log file and instead screenshotting random pieces of " + + "text you find in the console, you only make the process of resolving your mod problem longer." + Environment.NewLine + Environment.NewLine + + + "If you notice issues with a mod while playing, " + + "head to the Modding Discord by clicking on the button below, " + + "post the log file in the tech-support channel and wait for help." + Environment.NewLine + Environment.NewLine + + + "If you are a mod developer and wish to enable it back, go to Settings, and tick Enable Developer Tools." + Environment.NewLine + + "If you like the old conhost console style, you can enable it by opening the BepInEx/config/BepInEx.cfg and " + + "setting to true the \"Enables showing a console for log output.\" config option."; + } +} diff --git a/BepInEx.GUI/ViewModels/GeneralViewModel.cs b/BepInEx.GUI/ViewModels/GeneralViewModel.cs index 1174cbf..900d867 100644 --- a/BepInEx.GUI/ViewModels/GeneralViewModel.cs +++ b/BepInEx.GUI/ViewModels/GeneralViewModel.cs @@ -1,9 +1,13 @@ using BepInEx.GUI.Models; +using BepInEx.GUI.Models.Thunderstore; using ReactiveUI; -using System.Collections.Generic; +using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; +using System.Net; +using System.Net.Http; +using System.Text.Json; using WebSocketSharp; namespace BepInEx.GUI.ViewModels @@ -12,6 +16,8 @@ public class GeneralViewModel : ViewModelBase { public PathsInfo PathsInfo { get; } + public HttpClient HttpClient { get; } + public string TargetIsLoadingCanCloseWindow { get; } private string _loadedModCountText; @@ -34,10 +40,18 @@ public GeneralViewModel(PathsInfo pathsInfo, PlatformInfo platformInfo, WebSocke TargetIsLoadingCanCloseWindow = $"{pathsInfo.ProcessName} is loading, you can safely close this window."; + LoadedModCountText = "No plugins loaded."; + Mods = new ObservableCollection(); webSocket.OnMessage += AddLoadedModToList; PlatformInfo = platformInfo; + + var handler = new HttpClientHandler() + { + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate + }; + HttpClient = new HttpClient(handler); } private void AddLoadedModToList(object? sender, MessageEventArgs e) @@ -80,15 +94,17 @@ public void OnClickShowBepInExFolder() private void OpenFolder(string folderPath) { + if (!folderPath.EndsWith(Path.DirectorySeparatorChar)) + { + folderPath += Path.DirectorySeparatorChar; + } + if (Directory.Exists(folderPath)) { var processStartInfo = new ProcessStartInfo(); - processStartInfo.Arguments = folderPath; - - if (PlatformInfo.IsWindows) - { - processStartInfo.FileName = "explorer.exe"; - } + processStartInfo.FileName = folderPath; + processStartInfo.UseShellExecute = true; + processStartInfo.Verb = "open"; Process.Start(processStartInfo); } @@ -97,5 +113,52 @@ private void OpenFolder(string folderPath) Debug.Message($"{folderPath} Directory does not exist!"); } } + + public async void OnClickModdingDiscordLink() + { + try + { + var targetProcessName = PathsInfo.ProcessName.ToLowerInvariant(); + + var communities = JsonSerializer.Deserialize(await HttpClient.GetStringAsync("https://thunderstore.io/api/experimental/community/"))!; + var foundDiscord = false; + foreach (var community in communities.Results!) + { + var communityName = community.Name; + if (string.IsNullOrWhiteSpace(communityName)) + continue; + communityName = communityName.ToLowerInvariant(); + + if (community.DiscordUrl == null) + continue; + + var discordUrl = community.DiscordUrl.ToString(); + if (string.IsNullOrWhiteSpace(discordUrl)) + continue; + + if (communityName.Contains(targetProcessName) || targetProcessName.Contains(communityName)) + { + var processInfo = new ProcessStartInfo + { + FileName = discordUrl, + UseShellExecute = true + }; + + Process.Start(processInfo); + + foundDiscord = true; + } + } + + if (!foundDiscord) + { + Debug.Message("Did not find any discord for the following target process : " + targetProcessName); + } + } + catch (Exception ex) + { + Debug.Message(ex.ToString()); + } + } } } diff --git a/BepInEx.GUI/ViewModels/MainWindowViewModel.cs b/BepInEx.GUI/ViewModels/MainWindowViewModel.cs index e7ba444..0187af2 100644 --- a/BepInEx.GUI/ViewModels/MainWindowViewModel.cs +++ b/BepInEx.GUI/ViewModels/MainWindowViewModel.cs @@ -23,9 +23,9 @@ public MainWindowViewModel(WebSocket webSocket, PathsInfo pathsInfo, PlatformInf GeneralViewModel = new GeneralViewModel(PathsInfo, platformInfo, webSocket); - ConsoleViewModel = new ConsoleViewModel(webSocket, targetInfo); + ConsoleViewModel = new ConsoleViewModel(webSocket, targetInfo, platformInfo); - SettingsViewModel = new SettingsViewModel(PathsInfo, targetInfo); + SettingsViewModel = new SettingsViewModel(targetInfo); } } } diff --git a/BepInEx.GUI/ViewModels/SettingsViewModel.cs b/BepInEx.GUI/ViewModels/SettingsViewModel.cs index e2ac76c..86e6c14 100644 --- a/BepInEx.GUI/ViewModels/SettingsViewModel.cs +++ b/BepInEx.GUI/ViewModels/SettingsViewModel.cs @@ -20,7 +20,6 @@ public bool EnableDeveloperTools MainConfig.File.Save(); } } - private bool _closeWindowWhenGameLoaded; public bool CloseWindowWhenGameLoaded @@ -48,11 +47,9 @@ public bool CloseWindowWhenGameCloses #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. // dumb af compiler - public SettingsViewModel(PathsInfo pathsInfo, TargetInfo targetInfo) + public SettingsViewModel(TargetInfo targetInfo) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { - MainConfig.Init(pathsInfo.ConfigFilePath); - TargetInfo = targetInfo; SetConfigBindings(); diff --git a/BepInEx.GUI/Views/ConsoleView.axaml b/BepInEx.GUI/Views/ConsoleView.axaml index 4c318d2..dc31f79 100644 --- a/BepInEx.GUI/Views/ConsoleView.axaml +++ b/BepInEx.GUI/Views/ConsoleView.axaml @@ -9,12 +9,12 @@ - - - + + + - + @@ -22,23 +22,63 @@ -