diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs
index e33b473..12955a3 100644
--- a/Bloxstrap/Bootstrapper.cs
+++ b/Bloxstrap/Bootstrapper.cs
@@ -38,9 +38,9 @@ namespace Bloxstrap
private readonly CancellationTokenSource _cancelTokenSource = new();
private readonly IAppData AppData;
+ private readonly LaunchMode _launchMode;
private string _launchCommandLine = App.LaunchSettings.RobloxLaunchArgs;
- private LaunchMode _launchMode = App.LaunchSettings.RobloxLaunchMode;
private string _latestVersionGuid = null!;
private PackageManifest _versionPackageManifest = null!;
@@ -59,8 +59,10 @@ namespace Bloxstrap
#endregion
#region Core
- public Bootstrapper()
+ public Bootstrapper(LaunchMode launchMode)
{
+ _launchMode = launchMode;
+
// this is now always enabled as of v2.8.0
if (Dialog is not null)
Dialog.CancelEnabled = true;
@@ -382,18 +384,23 @@ namespace Bloxstrap
autoclosePids.Add(pid);
}
- string args = _appPid.ToString();
+ string argPids = _appPid.ToString();
if (autoclosePids.Any())
- args += $";{String.Join(',', autoclosePids)}";
+ argPids += $";{String.Join(',', autoclosePids)}";
- if (App.Settings.Prop.EnableActivityTracking || autoclosePids.Any())
+ if (App.Settings.Prop.EnableActivityTracking || App.LaunchSettings.TestModeFlag.Active || autoclosePids.Any())
{
using var ipl = new InterProcessLock("Watcher", TimeSpan.FromSeconds(5));
+ string args = $"-watcher \"{argPids}\"";
+
+ if (App.LaunchSettings.TestModeFlag.Active)
+ args += " -testmode";
+
// TODO: look into if this needs to be launched *before* roblox starts
if (ipl.IsAcquired)
- Process.Start(Paths.Process, $"-watcher \"{args}\"");
+ Process.Start(Paths.Process, args);
}
}
diff --git a/Bloxstrap/LaunchHandler.cs b/Bloxstrap/LaunchHandler.cs
index 891a7ed..090bd03 100644
--- a/Bloxstrap/LaunchHandler.cs
+++ b/Bloxstrap/LaunchHandler.cs
@@ -18,8 +18,7 @@ namespace Bloxstrap
break;
case NextAction.LaunchRoblox:
- App.LaunchSettings.RobloxLaunchMode = LaunchMode.Player;
- LaunchRoblox();
+ LaunchRoblox(LaunchMode.Player);
break;
default:
@@ -39,7 +38,7 @@ namespace Bloxstrap
else if (App.LaunchSettings.WatcherFlag.Active)
LaunchWatcher();
else if (App.LaunchSettings.RobloxLaunchMode != LaunchMode.None)
- LaunchRoblox();
+ LaunchRoblox(App.LaunchSettings.RobloxLaunchMode);
else if (!App.LaunchSettings.QuietFlag.Active)
LaunchMenu();
else
@@ -163,10 +162,13 @@ namespace Bloxstrap
ProcessNextAction(dialog.CloseAction);
}
- public static void LaunchRoblox()
+ public static void LaunchRoblox(LaunchMode launchMode)
{
const string LOG_IDENT = "LaunchHandler::LaunchRoblox";
+ if (launchMode == LaunchMode.None)
+ throw new InvalidOperationException("No Roblox launch mode set");
+
if (!File.Exists(Path.Combine(Paths.System, "mfplat.dll")))
{
Frontend.ShowMessageBox(Strings.Bootstrapper_WMFNotFound, MessageBoxImage.Error);
@@ -194,7 +196,7 @@ namespace Bloxstrap
// start bootstrapper and show the bootstrapper modal if we're not running silently
App.Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper");
- var bootstrapper = new Bootstrapper();
+ var bootstrapper = new Bootstrapper(launchMode);
IBootstrapperDialog? dialog = null;
if (!App.LaunchSettings.QuietFlag.Active)
diff --git a/Bloxstrap/LaunchSettings.cs b/Bloxstrap/LaunchSettings.cs
index 4bd10b1..4250dab 100644
--- a/Bloxstrap/LaunchSettings.cs
+++ b/Bloxstrap/LaunchSettings.cs
@@ -21,6 +21,8 @@ namespace Bloxstrap
public LaunchFlag UninstallFlag { get; } = new("uninstall");
public LaunchFlag NoLaunchFlag { get; } = new("nolaunch");
+
+ public LaunchFlag TestModeFlag { get; } = new("testmode");
public LaunchFlag NoGPUFlag { get; } = new("nogpu");
diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs
index 1d32a87..1c5b5d4 100644
--- a/Bloxstrap/Resources/Strings.Designer.cs
+++ b/Bloxstrap/Resources/Strings.Designer.cs
@@ -3241,6 +3241,28 @@ namespace Bloxstrap.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to Test mode.
+ ///
+ public static string Menu_TestMode {
+ get {
+ return ResourceManager.GetString("Menu.TestMode", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Test mode makes it easier to iteratively test how your settings affect Roblox.
+ ///
+ ///While enabled, it will automatically launch Roblox after closing Settings, and reopen Settings after closing Roblox, in a cycle until you disable it.
+ ///
+ ///Would you like to enable test mode?.
+ ///
+ public static string Menu_TestMode_Prompt {
+ get {
+ return ResourceManager.GetString("Menu.TestMode.Prompt", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Bloxstrap Settings.
///
diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx
index e7be39f..4043b82 100644
--- a/Bloxstrap/Resources/Strings.resx
+++ b/Bloxstrap/Resources/Strings.resx
@@ -1237,4 +1237,14 @@ Please manually delete Bloxstrap.exe from the install location or try restarting
Report exception
+
+ Test mode
+
+
+ Test mode makes it easier to iteratively test how your settings affect Roblox.
+
+While enabled, it will automatically launch Roblox after closing Settings, and reopen Settings after closing Roblox, in a cycle until you disable it.
+
+Would you like to enable test mode?
+
\ No newline at end of file
diff --git a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml
index 1b57bab..23e8d5e 100644
--- a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml
+++ b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml
@@ -10,9 +10,9 @@
xmlns:resources="clr-namespace:Bloxstrap.Resources"
mc:Ignorable="d"
d:DesignHeight="1500" d:DesignWidth="800"
+ d:DataContext="{d:DesignInstance dmodels:AboutViewModel, IsDesignTimeCreatable=True}"
Title="AboutPage"
Scrollable="True">
-
@@ -126,6 +126,7 @@
+
diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs
index d55c3dd..bf94311 100644
--- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs
+++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs
@@ -36,14 +36,14 @@ namespace Bloxstrap.UI.Elements.ContextMenu
_activityWatcher.OnLogOpen += ActivityWatcher_OnLogOpen;
_activityWatcher.OnGameJoin += ActivityWatcher_OnGameJoin;
_activityWatcher.OnGameLeave += ActivityWatcher_OnGameLeave;
+
+ if (!App.Settings.Prop.UseDisableAppPatch)
+ GameHistoryMenuItem.Visibility = Visibility.Visible;
}
if (_watcher.RichPresence is not null)
RichPresenceMenuItem.Visibility = Visibility.Visible;
- if (!App.Settings.Prop.UseDisableAppPatch)
- GameHistoryMenuItem.Visibility = Visibility.Visible;
-
VersionTextBlock.Text = $"{App.ProjectName} v{App.Version}";
}
diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml
index e0922d9..ffb58d5 100644
--- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml
+++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml
@@ -7,7 +7,9 @@
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:base="clr-namespace:Bloxstrap.UI.Elements.Base"
xmlns:resources="clr-namespace:Bloxstrap.Resources"
+ xmlns:dmodels="clr-namespace:Bloxstrap.UI.ViewModels.Settings"
mc:Ignorable="d"
+ d:DataContext="{d:DesignInstance dmodels:MainWindowViewModel, IsDesignTimeCreatable=True}"
Title="{x:Static resources:Strings.Menu_Title}"
MinWidth="960"
Width="980"
@@ -16,7 +18,8 @@
ExtendsContentIntoTitleBar="True"
WindowBackdropType="Mica"
WindowStartupLocation="CenterScreen"
- Closing="WpfUiWindow_Closing">
+ Closing="WpfUiWindow_Closing"
+ Closed="WpfUiWindow_Closed">
@@ -84,6 +87,7 @@
+
@@ -91,10 +95,16 @@
-
+
+
+
+
+
+
+
-
+
diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs
index 37e9237..3fcc316 100644
--- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs
+++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs
@@ -100,8 +100,13 @@ namespace Bloxstrap.UI.Elements.Settings
_state.Left = this.Left;
App.State.Save();
+ }
- if (!e.Cancel)
+ private void WpfUiWindow_Closed(object sender, EventArgs e)
+ {
+ if (App.LaunchSettings.TestModeFlag.Active)
+ LaunchHandler.LaunchRoblox(LaunchMode.Player);
+ else
App.SoftTerminate();
}
}
diff --git a/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs
index 5c04e50..a3826d3 100644
--- a/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs
+++ b/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs
@@ -1,4 +1,5 @@
-using System.Windows.Input;
+using System.Windows;
+using System.Windows.Input;
using Bloxstrap.UI.Elements.About;
using CommunityToolkit.Mvvm.Input;
@@ -16,6 +17,23 @@ namespace Bloxstrap.UI.ViewModels.Settings
public EventHandler? RequestCloseWindowEvent;
+ public bool TestModeEnabled
+ {
+ get => App.LaunchSettings.TestModeFlag.Active;
+ set
+ {
+ if (value)
+ {
+ var result = Frontend.ShowMessageBox(Strings.Menu_TestMode_Prompt, MessageBoxImage.Information, MessageBoxButton.YesNo);
+
+ if (result != MessageBoxResult.Yes)
+ return;
+ }
+
+ App.LaunchSettings.TestModeFlag.Active = value;
+ }
+ }
+
private void OpenAbout() => new MainWindow().ShowDialog();
private void CloseWindow() => RequestCloseWindowEvent?.Invoke(this, EventArgs.Empty);
diff --git a/Bloxstrap/Watcher.cs b/Bloxstrap/Watcher.cs
index 86859a8..ae67f8b 100644
--- a/Bloxstrap/Watcher.cs
+++ b/Bloxstrap/Watcher.cs
@@ -69,7 +69,7 @@ namespace Bloxstrap
if (App.Settings.Prop.UseDisableAppPatch)
{
- ActivityWatcher.OnAppClose += (_, _) =>
+ ActivityWatcher.OnAppClose += delegate
{
App.Logger.WriteLine(LOG_IDENT, "Received desktop app exit, closing Roblox");
using var process = Process.GetProcessById(_gameClientPid);
@@ -125,6 +125,9 @@ namespace Bloxstrap
foreach (int pid in _autoclosePids)
CloseProcess(pid);
+
+ if (App.LaunchSettings.TestModeFlag.Active)
+ Process.Start(Paths.Process, "-settings -testmode");
}
public void Dispose()