diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs
index cfd4ed9b..d7603903 100644
--- a/Bloxstrap/App.xaml.cs
+++ b/Bloxstrap/App.xaml.cs
@@ -49,6 +49,8 @@ public partial class App : Application
)
);
+ private static bool _showingExceptionDialog = false;
+
public static void Terminate(ErrorCode exitCode = ErrorCode.ERROR_SUCCESS)
{
if (IsFirstRun)
@@ -85,6 +87,11 @@ public static void FinalizeExceptionHandling(Exception exception, bool log = tru
#if DEBUG
throw exception;
#else
+ if (_showingExceptionDialog)
+ return;
+
+ _showingExceptionDialog = true;
+
if (!IsQuiet)
Controls.ShowExceptionDialog(exception);
@@ -148,33 +155,6 @@ protected override void OnStartup(StartupEventArgs e)
IsUpgrade = true;
}
}
-
- if (!IsMenuLaunch)
- {
- Logger.WriteLine(LOG_IDENT, "Performing connectivity check...");
-
- try
- {
- HttpClient.GetAsync("https://detectportal.firefox.com").Wait();
- Logger.WriteLine(LOG_IDENT, "Connectivity check finished");
- }
- catch (Exception ex)
- {
- Logger.WriteLine(LOG_IDENT, "Connectivity check failed!");
- Logger.WriteException(LOG_IDENT, ex);
-
- if (ex.GetType() == typeof(AggregateException))
- ex = ex.InnerException!;
-
- Controls.ShowConnectivityDialog(
- "the internet",
- $"Something may be preventing {ProjectName} from connecting to the internet, or you are currently offline. Please check and try again.",
- ex
- );
-
- Terminate(ErrorCode.ERROR_CANCELLED);
- }
- }
using (var checker = new InstallChecker())
{
diff --git a/Bloxstrap/Bloxstrap.csproj b/Bloxstrap/Bloxstrap.csproj
index 2257c751..48fc5dc0 100644
--- a/Bloxstrap/Bloxstrap.csproj
+++ b/Bloxstrap/Bloxstrap.csproj
@@ -7,8 +7,8 @@
true
True
Bloxstrap.ico
- 2.5.0
- 2.5.0.0
+ 2.5.1
+ 2.5.1.0
app.manifest
diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs
index 024fa9b7..0302340a 100644
--- a/Bloxstrap/Bootstrapper.cs
+++ b/Bloxstrap/Bootstrapper.cs
@@ -60,6 +60,7 @@ public class Bootstrapper
private string _latestVersionGuid = null!;
private PackageManifest _versionPackageManifest = null!;
+ private FileManifest _versionFileManifest = null!;
private string _versionFolder = null!;
private bool _isInstalling = false;
@@ -114,6 +115,39 @@ public async Task Run()
return;
}
+ // connectivity check
+
+ App.Logger.WriteLine(LOG_IDENT, "Performing connectivity check...");
+
+ SetStatus("Connecting to Roblox...");
+
+ try
+ {
+ await RobloxDeployment.GetInfo(RobloxDeployment.DefaultChannel);
+ }
+ catch (Exception ex)
+ {
+ App.Logger.WriteLine(LOG_IDENT, "Connectivity check failed!");
+ App.Logger.WriteException(LOG_IDENT, ex);
+
+ string message = $"It's possible that something is preventing {App.ProjectName} from connecting to the internet. Please check and try again.";
+
+ if (ex.GetType() == typeof(HttpResponseException))
+ message = "Roblox may be down right now. See status.roblox.com for more information. Please try again later.";
+ else if (ex.GetType() == typeof(TaskCanceledException))
+ message = "Bloxstrap timed out when trying to connect to three different Roblox deployment mirrors, indicating a poor internet connection. Please try again later.";
+ else if (ex.GetType() == typeof(AggregateException))
+ ex = ex.InnerException!;
+
+ Controls.ShowConnectivityDialog("Roblox", message, ex);
+
+ App.Terminate(ErrorCode.ERROR_CANCELLED);
+ }
+ finally
+ {
+ App.Logger.WriteLine(LOG_IDENT, "Connectivity check finished");
+ }
+
#if !DEBUG
if (!App.IsFirstRun && App.Settings.Prop.CheckForUpdates)
await CheckForUpdates();
@@ -126,8 +160,9 @@ public async Task Run()
try
{
- Mutex.OpenExisting("Bloxstrap_BootstrapperMutex").Close();
- App.Logger.WriteLine(LOG_IDENT, "Bloxstrap_BootstrapperMutex mutex exists, waiting...");
+ Mutex.OpenExisting("Bloxstrap_SingletonMutex").Close();
+ App.Logger.WriteLine(LOG_IDENT, "Bloxstrap_SingletonMutex mutex exists, waiting...");
+ SetStatus("Waiting for other instances...");
mutexExists = true;
}
catch (Exception)
@@ -136,7 +171,7 @@ public async Task Run()
}
// wait for mutex to be released if it's not yet
- await using AsyncMutex mutex = new("Bloxstrap_BootstrapperMutex");
+ await using var mutex = new AsyncMutex(true, "Bloxstrap_SingletonMutex");
await mutex.AcquireAsync(_cancelTokenSource.Token);
// reload our configs since they've likely changed by now
@@ -148,8 +183,6 @@ public async Task Run()
await CheckLatestVersion();
- CheckInstallMigration();
-
// install/update roblox if we're running for the first time, needs updating, or the player location doesn't exist
if (App.IsFirstRun || _latestVersionGuid != App.State.Prop.VersionGuid || !File.Exists(_playerLocation))
await InstallLatestVersion();
@@ -187,7 +220,7 @@ public async Task Run()
private async Task CheckLatestVersion()
{
- SetStatus("Connecting to Roblox...");
+ const string LOG_IDENT = "Bootstrapper::CheckLatestVersion";
ClientVersion clientVersion;
@@ -195,17 +228,14 @@ private async Task CheckLatestVersion()
{
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel);
}
- catch (Exception ex)
+ catch (HttpResponseException ex)
{
- string message = "It's possible that Roblox is being blocked by a firewall. Please check and try again.";
-
- if (ex.GetType() == typeof(HttpResponseException))
- message = "Roblox may be down right now. See status.roblox.com for more information. Please try again later.";
+ if (ex.ResponseMessage.StatusCode != HttpStatusCode.NotFound)
+ throw;
- Controls.ShowConnectivityDialog("Roblox", message, ex);
-
- App.Terminate(ErrorCode.ERROR_CANCELLED);
- return;
+ App.Logger.WriteLine(LOG_IDENT, $"Reverting enrolled channel to {RobloxDeployment.DefaultChannel} because a WindowsPlayer build does not exist for {App.Settings.Prop.Channel}");
+ App.Settings.Prop.Channel = RobloxDeployment.DefaultChannel;
+ clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel);
}
if (clientVersion.IsBehindDefaultChannel)
@@ -235,6 +265,7 @@ private async Task CheckLatestVersion()
_latestVersionGuid = clientVersion.VersionGuid;
_versionFolder = Path.Combine(Paths.Versions, _latestVersionGuid);
_versionPackageManifest = await PackageManifest.Get(_latestVersionGuid);
+ _versionFileManifest = await FileManifest.Get(_latestVersionGuid);
}
private async Task StartRoblox()
@@ -266,8 +297,12 @@ private async Task StartRoblox()
_launchCommandLine = _launchCommandLine.Replace("LAUNCHTIMEPLACEHOLDER", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString());
- if (App.Settings.Prop.Channel.ToLowerInvariant() != RobloxDeployment.DefaultChannel.ToLowerInvariant())
- _launchCommandLine += " -channel " + App.Settings.Prop.Channel.ToLowerInvariant();
+ _launchCommandLine += " -channel ";
+
+ if (App.Settings.Prop.Channel.ToLowerInvariant() == RobloxDeployment.DefaultChannel.ToLowerInvariant())
+ _launchCommandLine += "production";
+ else
+ _launchCommandLine += App.Settings.Prop.Channel.ToLowerInvariant();
// whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes
bool shouldWait = false;
@@ -391,6 +426,8 @@ public void CancelInstall()
App.Logger.WriteException(LOG_IDENT, ex);
}
+ Dialog?.CloseBootstrapper();
+
App.Terminate(ErrorCode.ERROR_CANCELLED);
}
#endregion
@@ -444,71 +481,6 @@ public void RegisterProgramSize()
App.Logger.WriteLine(LOG_IDENT, $"Registered as {totalSize} KB");
}
- private void CheckInstallMigration()
- {
- const string LOG_IDENT = "Bootstrapper::CheckInstallMigration";
-
- // check if we've changed our install location since the last time we started
- // in which case, we'll have to copy over all our folders so we don't lose any mods and stuff
-
- using RegistryKey? applicationKey = Registry.CurrentUser.OpenSubKey($@"Software\{App.ProjectName}", true);
-
- string? oldInstallLocation = (string?)applicationKey?.GetValue("OldInstallLocation");
-
- if (applicationKey is null || oldInstallLocation is null || oldInstallLocation == Paths.Base)
- return;
-
- SetStatus("Migrating install location...");
-
- if (Directory.Exists(oldInstallLocation))
- {
- App.Logger.WriteLine(LOG_IDENT, $"Moving all files in {oldInstallLocation} to {Paths.Base}...");
-
- foreach (string oldFileLocation in Directory.GetFiles(oldInstallLocation, "*.*", SearchOption.AllDirectories))
- {
- string relativeFile = oldFileLocation.Substring(oldInstallLocation.Length + 1);
- string newFileLocation = Path.Combine(Paths.Base, relativeFile);
- string? newDirectory = Path.GetDirectoryName(newFileLocation);
-
- try
- {
- if (!String.IsNullOrEmpty(newDirectory))
- Directory.CreateDirectory(newDirectory);
-
- File.Move(oldFileLocation, newFileLocation, true);
- }
- catch (Exception ex)
- {
- App.Logger.WriteLine(LOG_IDENT, $"Failed to move {oldFileLocation} to {newFileLocation}! {ex}");
- }
- }
-
- try
- {
- Directory.Delete(oldInstallLocation, true);
- App.Logger.WriteLine(LOG_IDENT, "Deleted old install location");
- }
- catch (Exception ex)
- {
- App.Logger.WriteLine(LOG_IDENT, $"Failed to delete old install location! {ex}");
- }
- }
-
- applicationKey.DeleteValue("OldInstallLocation");
-
- // allow shortcuts to be re-registered
- if (Directory.Exists(Paths.StartMenu))
- Directory.Delete(Paths.StartMenu, true);
-
- if (File.Exists(DesktopShortcutLocation))
- {
- File.Delete(DesktopShortcutLocation);
- App.Settings.Prop.CreateDesktopIcon = true;
- }
-
- App.Logger.WriteLine(LOG_IDENT, "Finished migrating install location!");
- }
-
public static void CheckInstall()
{
const string LOG_IDENT = "Bootstrapper::CheckInstall";
@@ -713,7 +685,7 @@ private void Uninstall()
// if the folder we're installed to does not end with "Bloxstrap", we're installed to a user-selected folder
// in which case, chances are they chose to install to somewhere they didn't really mean to (prior to the added warning in 2.4.0)
// if so, we're walking on eggshells and have to ensure we only clean up what we need to clean up
- bool cautiousUninstall = !Paths.Base.EndsWith(App.ProjectName);
+ bool cautiousUninstall = !Paths.Base.ToLower().EndsWith(App.ProjectName.ToLower());
var cleanupSequence = new List
{
@@ -783,7 +755,7 @@ private void Uninstall()
};
}
- Dialog?.ShowSuccess($"{App.ProjectName} has succesfully uninstalled", callback);
+ Dialog?.ShowSuccess($"{App.ProjectName} has successfully uninstalled", callback);
}
#endregion
@@ -906,7 +878,16 @@ private async Task InstallLatestVersion()
continue;
App.Logger.WriteLine(LOG_IDENT, $"Removing old version folder for {dir.Name}");
- dir.Delete(true);
+
+ try
+ {
+ dir.Delete(true);
+ }
+ catch (Exception ex)
+ {
+ App.Logger.WriteLine(LOG_IDENT, "Failed to delete version folder!");
+ App.Logger.WriteException(LOG_IDENT, ex);
+ }
}
}
}
@@ -999,6 +980,12 @@ private async Task ApplyModifications()
{
const string LOG_IDENT = "Bootstrapper::ApplyModifications";
+ if (Process.GetProcessesByName("RobloxPlayerBeta").Where(x => x.MainModule!.FileName == _playerLocation).Any())
+ {
+ App.Logger.WriteLine(LOG_IDENT, "Roblox is running, aborting mod check");
+ return;
+ }
+
SetStatus("Applying Roblox modifications...");
// set executable flags for fullscreen optimizations
@@ -1332,6 +1319,9 @@ private async Task DownloadPackage(Package package)
for (int i = 1; i <= maxTries; i++)
{
+ if (_cancelFired)
+ return;
+
int totalBytesRead = 0;
try
@@ -1370,7 +1360,8 @@ private async Task DownloadPackage(Package package)
App.Logger.WriteLine(LOG_IDENT, $"An exception occurred after downloading {totalBytesRead} bytes. ({i}/{maxTries})");
App.Logger.WriteException(LOG_IDENT, ex);
- File.Delete(packageLocation);
+ if (File.Exists(packageLocation))
+ File.Delete(packageLocation);
if (i >= maxTries)
throw;
@@ -1415,6 +1406,16 @@ private async Task ExtractPackage(Package package)
if (directory is not null)
Directory.CreateDirectory(directory);
+ if (File.Exists(extractPath))
+ {
+ var fileManifest = _versionFileManifest.FirstOrDefault(x => x.Name == Path.Combine(PackageDirectories[package.Name], entry.FullName));
+
+ if (fileManifest is not null && MD5Hash.FromFile(extractPath) == fileManifest.Signature)
+ continue;
+
+ File.Delete(extractPath);
+ }
+
entry.ExtractToFile(extractPath, true);
}
diff --git a/Bloxstrap/FastFlagManager.cs b/Bloxstrap/FastFlagManager.cs
index 0a3e36a8..6f435b7b 100644
--- a/Bloxstrap/FastFlagManager.cs
+++ b/Bloxstrap/FastFlagManager.cs
@@ -14,6 +14,8 @@ public class FastFlagManager : JsonManager>
public static IReadOnlyDictionary PresetFlags = new Dictionary
{
+ { "Network.Log", "FLogNetwork" },
+
{ "HTTP.Log", "DFLogHttpTraceLight" },
{ "HTTP.Proxy.Enable", "DFFlagDebugEnableHttpProxy" },
@@ -25,7 +27,6 @@ public class FastFlagManager : JsonManager>
{ "Rendering.ManualFullscreen", "FFlagHandleAltEnterFullscreenManually" },
{ "Rendering.TexturePack", "FStringPartTexturePackTable2022" },
{ "Rendering.DisableScaling", "DFFlagDisableDPIScale" },
- { "Rendering.MSAA", "FIntDebugForceMSAASamples" },
{ "Rendering.Mode.D3D11", "FFlagDebugGraphicsPreferD3D11" },
{ "Rendering.Mode.D3D10", "FFlagDebugGraphicsPreferD3D11FL10" },
@@ -43,7 +44,12 @@ public class FastFlagManager : JsonManager>
{ "UI.Menu.GraphicsSlider", "FFlagFixGraphicsQuality" },
{ "UI.Menu.Style.DisableV2", "FFlagDisableNewIGMinDUA" },
- { "UI.Menu.Style.EnableV4", "FFlagEnableInGameMenuControls" }
+ { "UI.Menu.Style.EnableV4", "FFlagEnableInGameMenuControls" },
+
+ { "UI.Menu.Style.ABTest.1", "FFlagEnableMenuControlsABTest" },
+ { "UI.Menu.Style.ABTest.2", "FFlagEnableMenuModernizationABTest" },
+ { "UI.Menu.Style.ABTest.3", "FFlagEnableMenuModernizationABTest2" },
+ { "UI.Menu.Style.ABTest.4", "FFlagEnableV3MenuABTest3" }
};
// only one missing here is Metal because lol
@@ -82,7 +88,8 @@ public class FastFlagManager : JsonManager>
new Dictionary
{
{ "DisableV2", null },
- { "EnableV4", null }
+ { "EnableV4", null },
+ { "ABTest", null }
}
},
@@ -91,7 +98,8 @@ public class FastFlagManager : JsonManager>
new Dictionary
{
{ "DisableV2", "True" },
- { "EnableV4", "False" }
+ { "EnableV4", "False" },
+ { "ABTest", "False" }
}
},
@@ -100,7 +108,8 @@ public class FastFlagManager : JsonManager>
new Dictionary
{
{ "DisableV2", "False" },
- { "EnableV4", "False" }
+ { "EnableV4", "False" },
+ { "ABTest", "False" }
}
},
@@ -109,7 +118,8 @@ public class FastFlagManager : JsonManager>
new Dictionary
{
{ "DisableV2", "True" },
- { "EnableV4", "True" }
+ { "EnableV4", "True" },
+ { "ABTest", "False" }
}
}
};
@@ -205,6 +215,11 @@ public override void Load()
CheckManualFullscreenPreset();
+ // TODO - remove when activity tracking has been revamped
+ if (GetPreset("Network.Log") != "7")
+ SetPreset("Network.Log", "7");
+
+
if (GetPreset("Rendering.Framerate") is not null)
return;
diff --git a/Bloxstrap/GlobalUsings.cs b/Bloxstrap/GlobalUsings.cs
index 105c8d91..15c873c5 100644
--- a/Bloxstrap/GlobalUsings.cs
+++ b/Bloxstrap/GlobalUsings.cs
@@ -18,8 +18,9 @@
global using Bloxstrap.Exceptions;
global using Bloxstrap.Extensions;
global using Bloxstrap.Models;
-global using Bloxstrap.Models.BloxstrapRPC;
global using Bloxstrap.Models.Attributes;
+global using Bloxstrap.Models.BloxstrapRPC;
global using Bloxstrap.Models.RobloxApi;
+global using Bloxstrap.Models.Manifest;
global using Bloxstrap.UI;
global using Bloxstrap.Utility;
\ No newline at end of file
diff --git a/Bloxstrap/InstallChecker.cs b/Bloxstrap/InstallChecker.cs
index 285d61dd..7e4bb096 100644
--- a/Bloxstrap/InstallChecker.cs
+++ b/Bloxstrap/InstallChecker.cs
@@ -1,5 +1,4 @@
using System.Windows;
-
using Microsoft.Win32;
namespace Bloxstrap
@@ -29,9 +28,23 @@ internal void Check()
return;
}
+ App.Logger.WriteLine(LOG_IDENT, "Installation registry key is likely malformed");
+
_installLocation = Path.GetDirectoryName(Paths.Process)!;
- App.Logger.WriteLine(LOG_IDENT, $"Registry key is likely malformed. Setting install location as '{_installLocation}'");
+ var result = Controls.ShowMessageBox(
+ $"It appears as if {App.ProjectName} hasn't been properly installed. Is it supposed to be installed at {_installLocation}?",
+ MessageBoxImage.Warning,
+ MessageBoxButton.YesNo
+ );
+
+ if (result != MessageBoxResult.Yes)
+ {
+ FirstTimeRun();
+ return;
+ }
+
+ App.Logger.WriteLine(LOG_IDENT, $"Setting install location as '{_installLocation}'");
if (_registryKey is null)
_registryKey = Registry.CurrentUser.CreateSubKey($"Software\\{App.ProjectName}");
@@ -199,11 +212,23 @@ internal static void CheckUpgrade()
// update migrations
- if (App.BuildMetadata.CommitRef.StartsWith("tag") && existingVersionInfo.ProductVersion == "2.4.0")
+ if (App.BuildMetadata.CommitRef.StartsWith("tag"))
{
- App.FastFlags.SetValue("DFFlagDisableDPIScale", null);
- App.FastFlags.SetValue("DFFlagVariableDPIScale2", null);
- App.FastFlags.Save();
+ if (existingVersionInfo.ProductVersion == "2.4.0")
+ {
+ App.FastFlags.SetValue("DFFlagDisableDPIScale", null);
+ App.FastFlags.SetValue("DFFlagVariableDPIScale2", null);
+ App.FastFlags.Save();
+ }
+ else if (existingVersionInfo.ProductVersion == "2.5.0")
+ {
+ App.FastFlags.SetValue("FIntDebugForceMSAASamples", null);
+
+ if (App.FastFlags.GetPreset("UI.Menu.Style.DisableV2") is not null)
+ App.FastFlags.SetPreset("UI.Menu.Style.ABTest", false);
+
+ App.FastFlags.Save();
+ }
}
if (isAutoUpgrade)
diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs
index 1444b369..ab920d12 100644
--- a/Bloxstrap/Integrations/DiscordRichPresence.cs
+++ b/Bloxstrap/Integrations/DiscordRichPresence.cs
@@ -9,6 +9,7 @@ public class DiscordRichPresence : IDisposable
private DiscordRPC.RichPresence? _currentPresence;
private DiscordRPC.RichPresence? _currentPresenceCopy;
+ private Message? _stashedRPCMessage;
private bool _visible = true;
private long _currentUniverseId;
@@ -55,6 +56,13 @@ public void ProcessRPCMessage(Message message)
if (_currentPresence is null || _currentPresenceCopy is null)
{
+ if (_activityWatcher.ActivityInGame)
+ {
+ App.Logger.WriteLine(LOG_IDENT, "Presence is not yet set, but is currently in game, stashing presence set request");
+ _stashedRPCMessage = message;
+ return;
+ }
+
App.Logger.WriteLine(LOG_IDENT, "Presence is not set, aborting");
return;
}
@@ -173,7 +181,10 @@ public async Task SetCurrentGame()
if (!_activityWatcher.ActivityInGame)
{
App.Logger.WriteLine(LOG_IDENT, "Not in game, clearing presence");
- _currentPresence = _currentPresenceCopy = null;
+
+ _currentPresence = _currentPresenceCopy = null;
+ _stashedRPCMessage = null;
+
UpdatePresence();
return true;
}
@@ -250,6 +261,9 @@ public async Task SetCurrentGame()
_ => $"by {universeDetails.Creator.Name}" + (universeDetails.Creator.HasVerifiedBadge ? " ☑️" : ""),
};
+ if (universeDetails.Name.Length < 2)
+ universeDetails.Name = $"{universeDetails.Name}\x2800\x2800\x2800";
+
_currentPresence = new DiscordRPC.RichPresence
{
Details = $"Playing {universeDetails.Name}",
@@ -268,7 +282,16 @@ public async Task SetCurrentGame()
// this is used for configuration from BloxstrapRPC
_currentPresenceCopy = _currentPresence.Clone();
- UpdatePresence();
+ if (_stashedRPCMessage is not null)
+ {
+ App.Logger.WriteLine(LOG_IDENT, "Found stashed RPC message, invoking presence set command now");
+ ProcessRPCMessage(_stashedRPCMessage);
+ _stashedRPCMessage = null;
+ }
+ else
+ {
+ UpdatePresence();
+ }
return true;
}
diff --git a/Bloxstrap/Logger.cs b/Bloxstrap/Logger.cs
index b2760a1e..e193c322 100644
--- a/Bloxstrap/Logger.cs
+++ b/Bloxstrap/Logger.cs
@@ -43,6 +43,7 @@ public void Initialize(bool useTempDir = false)
catch (IOException)
{
WriteLine(LOG_IDENT, "Failed to initialize because log file already exists");
+ return;
}
@@ -64,7 +65,16 @@ public void Initialize(bool useTempDir = false)
continue;
WriteLine(LOG_IDENT, $"Cleaning up old log file '{log.Name}'");
- log.Delete();
+
+ try
+ {
+ log.Delete();
+ }
+ catch (Exception ex)
+ {
+ WriteLine(LOG_IDENT, "Failed to delete log!");
+ WriteException(LOG_IDENT, ex);
+ }
}
}
}
diff --git a/Bloxstrap/Models/Manifest/FileManifest.cs b/Bloxstrap/Models/Manifest/FileManifest.cs
new file mode 100644
index 00000000..904f1ec3
--- /dev/null
+++ b/Bloxstrap/Models/Manifest/FileManifest.cs
@@ -0,0 +1,33 @@
+namespace Bloxstrap.Models.Manifest
+{
+ public class FileManifest : List
+ {
+ private FileManifest(string data)
+ {
+ using StringReader reader = new StringReader(data);
+
+ while (true)
+ {
+ string? fileName = reader.ReadLine();
+ string? signature = reader.ReadLine();
+
+ if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(signature))
+ break;
+
+ Add(new ManifestFile
+ {
+ Name = fileName,
+ Signature = signature
+ });
+ }
+ }
+
+ public static async Task Get(string versionGuid)
+ {
+ string pkgManifestUrl = RobloxDeployment.GetLocation($"/{versionGuid}-rbxManifest.txt");
+ var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl);
+
+ return new FileManifest(pkgManifestData);
+ }
+ }
+}
diff --git a/Bloxstrap/Models/Manifest/ManifestFile.cs b/Bloxstrap/Models/Manifest/ManifestFile.cs
new file mode 100644
index 00000000..71b19767
--- /dev/null
+++ b/Bloxstrap/Models/Manifest/ManifestFile.cs
@@ -0,0 +1,13 @@
+namespace Bloxstrap.Models.Manifest
+{
+ public class ManifestFile
+ {
+ public string Name { get; set; } = "";
+ public string Signature { get; set; } = "";
+
+ public override string ToString()
+ {
+ return $"[{Signature}] {Name}";
+ }
+ }
+}
diff --git a/Bloxstrap/Models/Package.cs b/Bloxstrap/Models/Manifest/Package.cs
similarity index 92%
rename from Bloxstrap/Models/Package.cs
rename to Bloxstrap/Models/Manifest/Package.cs
index 8b6be56e..6feb162e 100644
--- a/Bloxstrap/Models/Package.cs
+++ b/Bloxstrap/Models/Manifest/Package.cs
@@ -4,7 +4,7 @@
* Copyright (c) 2015-present MaximumADHD
*/
-namespace Bloxstrap.Models
+namespace Bloxstrap.Models.Manifest
{
public class Package
{
diff --git a/Bloxstrap/Models/PackageManifest.cs b/Bloxstrap/Models/Manifest/PackageManifest.cs
similarity index 98%
rename from Bloxstrap/Models/PackageManifest.cs
rename to Bloxstrap/Models/Manifest/PackageManifest.cs
index 4d7c3a18..13c71728 100644
--- a/Bloxstrap/Models/PackageManifest.cs
+++ b/Bloxstrap/Models/Manifest/PackageManifest.cs
@@ -4,7 +4,7 @@
* Copyright (c) 2015-present MaximumADHD
*/
-namespace Bloxstrap.Models
+namespace Bloxstrap.Models.Manifest
{
public class PackageManifest : List
{
diff --git a/Bloxstrap/RobloxDeployment.cs b/Bloxstrap/RobloxDeployment.cs
index 30267d2a..cdeda888 100644
--- a/Bloxstrap/RobloxDeployment.cs
+++ b/Bloxstrap/RobloxDeployment.cs
@@ -7,7 +7,7 @@ public static class RobloxDeployment
private static Dictionary ClientVersionCache = new();
- // a list of roblox delpoyment locations that we check for, in case one of them don't work
+ // a list of roblox deployment locations that we check for, in case one of them don't work
private static List BaseUrls = new()
{
"https://setup.rbxcdn.com",
@@ -52,18 +52,6 @@ public static string BaseUrl
return _baseUrl;
}
}
-
- // most commonly used/interesting channels
- public static readonly List SelectableChannels = new()
- {
- "LIVE",
- "ZFlag",
- "ZNext",
- "ZCanary",
- "ZIntegration",
- "ZAvatarTeam",
- "ZSocialTeam"
- };
#endregion
public static string GetLocation(string resource, string? channel = null)
diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs
index d2f6a8aa..8354ced9 100644
--- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs
+++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs
@@ -96,7 +96,7 @@ private void Window_Loaded(object? sender, RoutedEventArgs e)
private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _richPresenceHandler?.SetVisibility(((MenuItem)sender).IsChecked);
- private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetText($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}");
+ private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}");
private void ServerDetailsMenuItem_Click(object sender, RoutedEventArgs e) => ShowServerInformationWindow();
diff --git a/Bloxstrap/UI/Elements/Dialogs/AddFastFlagDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/AddFastFlagDialog.xaml
index 06e7cc08..3a475280 100644
--- a/Bloxstrap/UI/Elements/Dialogs/AddFastFlagDialog.xaml
+++ b/Bloxstrap/UI/Elements/Dialogs/AddFastFlagDialog.xaml
@@ -33,10 +33,10 @@
-
+
-
+
diff --git a/Bloxstrap/UI/Elements/Dialogs/BulkAddFastFlagDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/BulkAddFastFlagDialog.xaml
new file mode 100644
index 00000000..4cef49fb
--- /dev/null
+++ b/Bloxstrap/UI/Elements/Dialogs/BulkAddFastFlagDialog.xaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Paste in your JSON here...
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Bloxstrap/UI/Elements/Dialogs/BulkAddFastFlagDialog.xaml.cs b/Bloxstrap/UI/Elements/Dialogs/BulkAddFastFlagDialog.xaml.cs
new file mode 100644
index 00000000..eccf4fe1
--- /dev/null
+++ b/Bloxstrap/UI/Elements/Dialogs/BulkAddFastFlagDialog.xaml.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace Bloxstrap.UI.Elements.Dialogs
+{
+ ///
+ /// Interaction logic for BulkAddFastFlagDialog.xaml
+ ///
+ public partial class BulkAddFastFlagDialog
+ {
+ public MessageBoxResult Result = MessageBoxResult.Cancel;
+
+ public BulkAddFastFlagDialog()
+ {
+ InitializeComponent();
+ }
+
+ private void OKButton_Click(object sender, RoutedEventArgs e)
+ {
+ Result = MessageBoxResult.OK;
+ Close();
+ }
+ }
+}
diff --git a/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs b/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs
index b34a9051..c16025a7 100644
--- a/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs
+++ b/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs
@@ -16,17 +16,12 @@ public partial class ConnectivityDialog
{
public ConnectivityDialog(string targetName, string description, Exception exception)
{
- Exception? innerException = exception.InnerException;
-
InitializeComponent();
TitleTextBlock.Text = $"{App.ProjectName} is unable to connect to {targetName}";
DescriptionTextBlock.Text = description;
- ErrorRichTextBox.Selection.Text = $"{exception.GetType()}: {exception.Message}";
-
- if (innerException is not null)
- ErrorRichTextBox.Selection.Text += $"\n\n===== Inner Exception =====\n{innerException.GetType()}: {innerException.Message}";
+ AddException(exception);
CloseButton.Click += delegate
{
@@ -41,5 +36,18 @@ public ConnectivityDialog(string targetName, string description, Exception excep
PInvoke.FlashWindow((HWND)hWnd, true);
};
}
+
+ private void AddException(Exception exception, bool inner = false)
+ {
+ if (!inner)
+ ErrorRichTextBox.Selection.Text = $"{exception.GetType()}: {exception.Message}";
+
+ if (exception.InnerException is null)
+ return;
+
+ ErrorRichTextBox.Selection.Text += $"\n\n[Inner Exception]\n{exception.InnerException.GetType()}: {exception.InnerException.Message}";
+
+ AddException(exception.InnerException, true);
+ }
}
}
diff --git a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml
index 54d8a51f..a2c54182 100644
--- a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml
+++ b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml
@@ -6,7 +6,7 @@
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:local="clr-namespace:Bloxstrap.UI.Elements.Dialogs"
mc:Ignorable="d"
- Width="480"
+ Width="540"
MinHeight="0"
SizeToContent="Height"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
diff --git a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs
index e53485d4..c5cff3a1 100644
--- a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs
+++ b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs
@@ -22,10 +22,8 @@ public ExceptionDialog(Exception exception)
InitializeComponent();
Title = RootTitleBar.Title = $"{App.ProjectName} Exception";
- ErrorRichTextBox.Selection.Text = $"{exception.GetType()}: {exception.Message}";
- if (innerException is not null)
- ErrorRichTextBox.Selection.Text += $"\n\n===== Inner Exception =====\n{innerException.GetType()}: {innerException.Message}";
+ AddException(exception);
if (!App.Logger.Initialized)
LocateLogFileButton.Content = "Copy log contents";
@@ -35,7 +33,7 @@ public ExceptionDialog(Exception exception)
if (App.Logger.Initialized)
Process.Start("explorer.exe", $"/select,\"{App.Logger.FileLocation}\"");
else
- Clipboard.SetText(String.Join("\r\n", App.Logger.Backlog));
+ Clipboard.SetDataObject(String.Join("\r\n", App.Logger.Backlog));
};
ReportOptions.DropDownClosed += (sender, e) =>
@@ -66,5 +64,18 @@ public ExceptionDialog(Exception exception)
PInvoke.FlashWindow((HWND)hWnd, true);
};
}
+
+ private void AddException(Exception exception, bool inner = false)
+ {
+ if (!inner)
+ ErrorRichTextBox.Selection.Text = $"{exception.GetType()}: {exception.Message}";
+
+ if (exception.InnerException is null)
+ return;
+
+ ErrorRichTextBox.Selection.Text += $"\n\n[Inner Exception]\n{exception.InnerException.GetType()}: {exception.InnerException.Message}";
+
+ AddException(exception.InnerException, true);
+ }
}
}
diff --git a/Bloxstrap/UI/Elements/Menu/MainWindow.xaml b/Bloxstrap/UI/Elements/Menu/MainWindow.xaml
index 73260ad1..6fee20ef 100644
--- a/Bloxstrap/UI/Elements/Menu/MainWindow.xaml
+++ b/Bloxstrap/UI/Elements/Menu/MainWindow.xaml
@@ -59,26 +59,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Bloxstrap/UI/Elements/Menu/MainWindow.xaml.cs b/Bloxstrap/UI/Elements/Menu/MainWindow.xaml.cs
index 989fa65b..968dabf7 100644
--- a/Bloxstrap/UI/Elements/Menu/MainWindow.xaml.cs
+++ b/Bloxstrap/UI/Elements/Menu/MainWindow.xaml.cs
@@ -15,16 +15,14 @@ namespace Bloxstrap.UI.Elements.Menu
public partial class MainWindow : INavigationWindow
{
private readonly IThemeService _themeService = new ThemeService();
- private readonly IDialogService _dialogService = new DialogService();
public MainWindow()
{
App.Logger.WriteLine("MainWindow::MainWindow", "Initializing menu");
- DataContext = new MainWindowViewModel(this, _dialogService);
+ DataContext = new MainWindowViewModel(this);
SetTheme();
InitializeComponent();
- _dialogService.SetDialogControl(RootDialog);
}
public void SetTheme()
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml b/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml
index 1e4b4aa8..68bdc7b1 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml
+++ b/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml
@@ -46,7 +46,7 @@
-
+
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml.cs b/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml.cs
index 68de8d7e..1a7631d1 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml.cs
+++ b/Bloxstrap/UI/Elements/Menu/Pages/BehaviourPage.xaml.cs
@@ -1,5 +1,4 @@
-using Bloxstrap.UI.ViewModels.Menu;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -14,6 +13,8 @@
using System.Windows.Navigation;
using System.Windows.Shapes;
+using Bloxstrap.UI.ViewModels.Menu;
+
namespace Bloxstrap.UI.Elements.Menu.Pages
{
///
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml b/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml
index f66edba0..d2447c8d 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml
+++ b/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml
@@ -17,7 +17,7 @@
-
+
@@ -87,7 +87,7 @@
-
+
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml.cs b/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml.cs
index fec43edc..01d37b6c 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml.cs
+++ b/Bloxstrap/UI/Elements/Menu/Pages/FastFlagEditorPage.xaml.cs
@@ -8,6 +8,7 @@
using Wpf.Ui.Mvvm.Contracts;
using Bloxstrap.UI.Elements.Dialogs;
+using System.Xml.Linq;
namespace Bloxstrap.UI.Elements.Menu.Pages
{
@@ -111,11 +112,28 @@ private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventA
break; */
case "Name":
- string newName = ((TextBox)e.EditingElement).Text;
+ var textbox = e.EditingElement as TextBox;
- App.FastFlags.SetValue(entry.Name, null);
+ string oldName = entry.Name;
+ string newName = textbox!.Text;
+
+ if (newName == oldName)
+ return;
+
+ if (App.FastFlags.GetValue(newName) is not null)
+ {
+ Controls.ShowMessageBox("A FastFlag with this name already exists.", MessageBoxImage.Information);
+ e.Cancel = true;
+ textbox.Text = oldName;
+ return;
+ }
+
+ App.FastFlags.SetValue(oldName, null);
App.FastFlags.SetValue(newName, entry.Value);
+ if (!newName.Contains(_searchFilter))
+ ClearSearch();
+
break;
case "Value":
@@ -215,58 +233,65 @@ private void ToggleButton_Click(object sender, RoutedEventArgs e)
private void ImportJSONButton_Click(object sender, RoutedEventArgs e)
{
- var dialog = new OpenFileDialog
- {
- Filter = "JSON files|*.json|All files|*.*"
- };
+ string json = "";
+ Dictionary? list = null;
- if (dialog.ShowDialog() != true)
- return;
+ while (list is null)
+ {
+ var dialog = new BulkAddFastFlagDialog();
+ dialog.JsonTextBox.Text = json;
+ dialog.ShowDialog();
- try
- {
- var list = JsonSerializer.Deserialize>(File.ReadAllText(dialog.FileName));
+ if (dialog.Result != MessageBoxResult.OK)
+ return;
- if (list is null)
- throw new Exception("JSON deserialization returned null");
+ json = dialog.JsonTextBox.Text;
- var conflictingFlags = App.FastFlags.Prop.Where(x => list.ContainsKey(x.Key)).Select(x => x.Key);
- bool overwriteConflicting = false;
+ try
+ {
+ list = JsonSerializer.Deserialize>(json);
- if (conflictingFlags.Any())
+ if (list is null)
+ throw new Exception("JSON deserialization returned null");
+ }
+ catch (Exception ex)
{
- var result = Controls.ShowMessageBox(
- "Some of the flags you are attempting to import already have set values. Would you like to overwrite their current values with the ones defined in the import?\n" +
+ Controls.ShowMessageBox(
+ "The JSON you've entered does not appear to be valid. Please double check it and try again.\n" +
"\n" +
- "Conflicting flags:\n" +
- String.Join(", ", conflictingFlags),
- MessageBoxImage.Question,
- MessageBoxButton.YesNo
+ "More information:\n" +
+ $"{ex.Message}",
+ MessageBoxImage.Error
);
-
- overwriteConflicting = result == MessageBoxResult.Yes;
}
+ }
- foreach (var pair in list)
- {
- if (App.FastFlags.Prop.ContainsKey(pair.Key) && !overwriteConflicting)
- continue;
+ var conflictingFlags = App.FastFlags.Prop.Where(x => list.ContainsKey(x.Key)).Select(x => x.Key);
+ bool overwriteConflicting = false;
- App.FastFlags.SetValue(pair.Key, pair.Value);
- }
+ if (conflictingFlags.Any())
+ {
+ var result = Controls.ShowMessageBox(
+ "Some of the flags you are attempting to import already have set values. Would you like to overwrite their current values with the ones defined in the import?\n" +
+ "\n" +
+ "Conflicting flags:\n" +
+ String.Join(", ", conflictingFlags),
+ MessageBoxImage.Question,
+ MessageBoxButton.YesNo
+ );
- ClearSearch();
+ overwriteConflicting = result == MessageBoxResult.Yes;
}
- catch (Exception ex)
+
+ foreach (var pair in list)
{
- Controls.ShowMessageBox(
- "The file you've selected does not appear to be valid JSON. Please double check the file contents and try again.\n" +
- "\n" +
- "More information:\n" +
- $"{ex.Message}",
- MessageBoxImage.Error
- );
+ if (App.FastFlags.Prop.ContainsKey(pair.Key) && !overwriteConflicting)
+ continue;
+
+ App.FastFlags.SetValue(pair.Key, pair.Value);
}
+
+ ClearSearch();
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/FastFlagsPage.xaml b/Bloxstrap/UI/Elements/Menu/Pages/FastFlagsPage.xaml
index 50c0501f..666e6652 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/FastFlagsPage.xaml
+++ b/Bloxstrap/UI/Elements/Menu/Pages/FastFlagsPage.xaml
@@ -171,15 +171,6 @@
-
-
-
-
-
-
-
-
-
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml b/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml
index 7f285030..3c34ffc3 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml
+++ b/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml
@@ -1,66 +1,79 @@
-
-
+ Title="InstallationPage">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml.cs b/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml.cs
index b58ea4a7..a46ae0e7 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml.cs
+++ b/Bloxstrap/UI/Elements/Menu/Pages/InstallationPage.xaml.cs
@@ -1,4 +1,19 @@
-using Bloxstrap.UI.ViewModels.Menu;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+using Bloxstrap.UI.ViewModels.Menu;
namespace Bloxstrap.UI.Elements.Menu.Pages
{
diff --git a/Bloxstrap/UI/Elements/Menu/Pages/IntegrationsPage.xaml b/Bloxstrap/UI/Elements/Menu/Pages/IntegrationsPage.xaml
index 3cda7658..a01e5a72 100644
--- a/Bloxstrap/UI/Elements/Menu/Pages/IntegrationsPage.xaml
+++ b/Bloxstrap/UI/Elements/Menu/Pages/IntegrationsPage.xaml
@@ -13,7 +13,7 @@
-
+
diff --git a/Bloxstrap/UI/NotifyIconWrapper.cs b/Bloxstrap/UI/NotifyIconWrapper.cs
index 87c6b833..6eb8e065 100644
--- a/Bloxstrap/UI/NotifyIconWrapper.cs
+++ b/Bloxstrap/UI/NotifyIconWrapper.cs
@@ -82,7 +82,7 @@ public async void OnGameJoin()
string serverLocation = await _activityWatcher!.GetServerLocation();
ShowAlert(
- $"Connnected to {_activityWatcher.ActivityServerType.ToString().ToLower()} server",
+ $"Connected to {_activityWatcher.ActivityServerType.ToString().ToLower()} server",
$"Located at {serverLocation}\nClick for more information",
10,
(_, _) => _menuContainer?.ShowServerInformationWindow()
@@ -124,7 +124,7 @@ public void ShowAlert(string caption, string message, int duration, EventHandler
if (_alertClickHandler == clickHandler)
_alertClickHandler = null;
else
- App.Logger.WriteLine(LOG_IDENT, "Click handler has been overriden by another alert");
+ App.Logger.WriteLine(LOG_IDENT, "Click handler has been overridden by another alert");
});
}
diff --git a/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs b/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs
index 576d8288..d6c477e7 100644
--- a/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs
+++ b/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs
@@ -30,6 +30,6 @@ public ServerInformationViewModel(Window window, ActivityWatcher activityWatcher
});
}
- private void CopyInstanceId() => Clipboard.SetText(InstanceId);
+ private void CopyInstanceId() => Clipboard.SetDataObject(InstanceId);
}
}
diff --git a/Bloxstrap/UI/ViewModels/Menu/BehaviourViewModel.cs b/Bloxstrap/UI/ViewModels/Menu/BehaviourViewModel.cs
index da81ace4..28c4bc78 100644
--- a/Bloxstrap/UI/ViewModels/Menu/BehaviourViewModel.cs
+++ b/Bloxstrap/UI/ViewModels/Menu/BehaviourViewModel.cs
@@ -81,18 +81,12 @@ public bool UpdateCheckingEnabled
set => App.Settings.Prop.CheckForUpdates = value;
}
- public IEnumerable Channels => RobloxDeployment.SelectableChannels;
-
public string SelectedChannel
{
get => App.Settings.Prop.Channel;
set
{
value = value.Trim();
-
- if (String.IsNullOrEmpty(value))
- value = RobloxDeployment.DefaultChannel;
-
Task.Run(() => LoadChannelDeployInfo(value));
App.Settings.Prop.Channel = value;
}
diff --git a/Bloxstrap/UI/ViewModels/Menu/FastFlagsViewModel.cs b/Bloxstrap/UI/ViewModels/Menu/FastFlagsViewModel.cs
index 28ba70dd..8dee2e5d 100644
--- a/Bloxstrap/UI/ViewModels/Menu/FastFlagsViewModel.cs
+++ b/Bloxstrap/UI/ViewModels/Menu/FastFlagsViewModel.cs
@@ -101,8 +101,11 @@ public string SelectedIGMenuVersion
foreach (var flag in version.Value)
{
- if (App.FastFlags.GetPreset($"UI.Menu.Style.{flag.Key}") != flag.Value)
- flagsMatch = false;
+ foreach (var presetFlag in FastFlagManager.PresetFlags.Where(x => x.Key.StartsWith($"UI.Menu.Style.{flag.Key}")))
+ {
+ if (App.FastFlags.GetValue(presetFlag.Value) != flag.Value)
+ flagsMatch = false;
+ }
}
if (flagsMatch)
@@ -127,14 +130,6 @@ public string SelectedLightingMode
set => App.FastFlags.SetPresetEnum("Rendering.Lighting", LightingModes[value], "True");
}
- public IReadOnlyDictionary MSAAModes => FastFlagManager.MSAAModes;
-
- public string SelectedMSAAMode
- {
- get => MSAAModes.First(x => x.Value == App.FastFlags.GetPreset("Rendering.MSAA")).Key ?? MSAAModes.First().Key;
- set => App.FastFlags.SetPreset("Rendering.MSAA", MSAAModes[value]);
- }
-
public bool GuiHidingEnabled
{
get => App.FastFlags.GetPreset("UI.Hide") == "32380007";
diff --git a/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs b/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs
index bc868bd7..0e520ec7 100644
--- a/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs
+++ b/Bloxstrap/UI/ViewModels/Menu/MainWindowViewModel.cs
@@ -14,8 +14,6 @@ namespace Bloxstrap.UI.ViewModels.Menu
public class MainWindowViewModel : NotifyPropertyChangedViewModel
{
private readonly Window _window;
- private readonly IDialogService _dialogService;
- private readonly string _originalBaseDirectory = App.BaseDirectory; // we need this to check if the basedirectory changes
public ICommand CloseWindowCommand => new RelayCommand(CloseWindow);
public ICommand ConfirmSettingsCommand => new RelayCommand(ConfirmSettings);
@@ -24,25 +22,31 @@ public class MainWindowViewModel : NotifyPropertyChangedViewModel
public string ConfirmButtonText => App.IsFirstRun ? "Install" : "Save";
public bool ConfirmButtonEnabled { get; set; } = true;
- public MainWindowViewModel(Window window, IDialogService dialogService)
+ public MainWindowViewModel(Window window)
{
_window = window;
- _dialogService = dialogService;
}
private void CloseWindow() => _window.Close();
private void ConfirmSettings()
{
+ if (!App.IsFirstRun)
+ {
+ App.ShouldSaveConfigs = true;
+ App.FastFlags.Save();
+ CloseWindow();
+
+ return;
+ }
+
if (string.IsNullOrEmpty(App.BaseDirectory))
{
Controls.ShowMessageBox("You must set an install location", MessageBoxImage.Error);
return;
}
- bool shouldCheckInstallLocation = App.IsFirstRun || App.BaseDirectory != _originalBaseDirectory;
-
- if (shouldCheckInstallLocation && NavigationVisibility == Visibility.Visible)
+ if (NavigationVisibility == Visibility.Visible)
{
try
{
@@ -87,54 +91,45 @@ private void ConfirmSettings()
else if (result == MessageBoxResult.Cancel)
return;
}
- }
- if (App.IsFirstRun)
- {
- if (NavigationVisibility == Visibility.Visible)
+ if (
+ App.BaseDirectory.Length <= 3 || // prevent from installing to the root of a drive
+ App.BaseDirectory.StartsWith("\\\\") || // i actually haven't encountered anyone doing this and i dont even know if this is possible but this is just to be safe lmao
+ App.BaseDirectory.ToLowerInvariant().Contains("onedrive") || // prevent from installing to a onedrive folder
+ Directory.GetParent(App.BaseDirectory)!.ToString().ToLowerInvariant() == Paths.UserProfile.ToLowerInvariant() // prevent from installing to an essential user profile folder
+ )
{
- ((INavigationWindow)_window).Navigate(typeof(PreInstallPage));
+ Controls.ShowMessageBox(
+ $"{App.ProjectName} cannot be installed here. Please choose a different location, or resort to using the default location by clicking the reset button.",
+ MessageBoxImage.Error,
+ MessageBoxButton.OK
+ );
- NavigationVisibility = Visibility.Collapsed;
- OnPropertyChanged(nameof(NavigationVisibility));
-
- ConfirmButtonEnabled = false;
- OnPropertyChanged(nameof(ConfirmButtonEnabled));
+ return;
+ }
+ }
+
+ if (NavigationVisibility == Visibility.Visible)
+ {
+ ((INavigationWindow)_window).Navigate(typeof(PreInstallPage));
- Task.Run(async delegate
- {
- await Task.Delay(3000);
+ NavigationVisibility = Visibility.Collapsed;
+ OnPropertyChanged(nameof(NavigationVisibility));
+
+ ConfirmButtonEnabled = false;
+ OnPropertyChanged(nameof(ConfirmButtonEnabled));
- ConfirmButtonEnabled = true;
- OnPropertyChanged(nameof(ConfirmButtonEnabled));
- });
- }
- else
+ Task.Run(async delegate
{
- App.IsSetupComplete = true;
- CloseWindow();
- }
+ await Task.Delay(3000);
+
+ ConfirmButtonEnabled = true;
+ OnPropertyChanged(nameof(ConfirmButtonEnabled));
+ });
}
else
{
- App.ShouldSaveConfigs = true;
- App.FastFlags.Save();
-
- if (shouldCheckInstallLocation)
- {
- App.Logger.WriteLine("MainWindowViewModel::ConfirmSettings", $"Changing install location from {_originalBaseDirectory} to {App.BaseDirectory}");
-
- Controls.ShowMessageBox(
- $"{App.ProjectName} will install to the new location you've set the next time it runs.",
- MessageBoxImage.Information
- );
-
- using RegistryKey registryKey = Registry.CurrentUser.CreateSubKey($@"Software\{App.ProjectName}");
- registryKey.SetValue("InstallLocation", App.BaseDirectory);
- registryKey.SetValue("OldInstallLocation", _originalBaseDirectory);
- Paths.Initialize(App.BaseDirectory);
- }
-
+ App.IsSetupComplete = true;
CloseWindow();
}
}
diff --git a/Bloxstrap/Utility/AsyncMutex.cs b/Bloxstrap/Utility/AsyncMutex.cs
index 400ccc89..0cbf2f7d 100644
--- a/Bloxstrap/Utility/AsyncMutex.cs
+++ b/Bloxstrap/Utility/AsyncMutex.cs
@@ -4,13 +4,15 @@
public sealed class AsyncMutex : IAsyncDisposable
{
+ private readonly bool _initiallyOwned;
private readonly string _name;
private Task? _mutexTask;
private ManualResetEventSlim? _releaseEvent;
private CancellationTokenSource? _cancellationTokenSource;
- public AsyncMutex(string name)
+ public AsyncMutex(bool initiallyOwned, string name)
{
+ _initiallyOwned = initiallyOwned;
_name = name;
}
@@ -31,7 +33,7 @@ public Task AcquireAsync(CancellationToken cancellationToken)
try
{
CancellationToken cancellationToken = _cancellationTokenSource.Token;
- using var mutex = new Mutex(false, _name);
+ using var mutex = new Mutex(_initiallyOwned, _name);
try
{
// Wait for either the mutex to be acquired, or cancellation