Merge branch 'main' into feature/improve-studio-support

This commit is contained in:
bluepilledgreat 2024-11-05 21:13:13 +00:00
commit eb9e1341c7
48 changed files with 808 additions and 325 deletions

View File

@ -5,6 +5,10 @@ body:
- type: markdown
attributes:
value: |
> [!IMPORTANT]
> Do NOT open an issue if you're getting an error saying "`The given key 'redist.zip' was not present in the dictionary.`", or if you're getting stuck on "Configuring Roblox...".
> This problem has been fixed in the latest version of Bloxstrap. You either have auto-updates disabled or are using someone's custom build of an old version.
> Download the latest version [here](https://github.com/pizzaboxer/bloxstrap/releases/latest).
### **Preliminary instructions**
- Before opening an issue, please [check the Wiki first](https://github.com/pizzaboxer/bloxstrap/wiki/) to see if your problem has been addressed there.
- If it isn't, please confirm which pages that you read that were relevant to your issue.
@ -26,6 +30,14 @@ body:
- label: I am using the latest version of Bloxstrap.
required: true
- label: I did not answer truthfully to all the above checkboxes.
- type: input
id: version
attributes:
label: Bloxstrap Version
description: "What version of Bloxstrap are you using? Find it in the 'About' section of the Settings"
placeholder: "v1.0.0"
validations:
required: true
- type: textarea
id: what-happened
attributes:
@ -39,4 +51,4 @@ body:
label: Bloxstrap Log
description: If you're getting a Bloxstrap Exception error, upload your log file here. Otherwise, just leave it empty.
value: "N/A"
#render: text
render: text

View File

@ -39,23 +39,17 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v')
steps:
# - name: Sign and download artifact
# uses: signpath/github-action-submit-signing-request@v1
# with:
# api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
# organization-id: '107b3de5-057b-42fc-a985-3546e4261775'
# project-slug: 'bloxstrap'
# signing-policy-slug: 'release-signing'
# artifact-configuration-slug: 'github-ci'
# github-artifact-id: '${{ needs.build.outputs.artifact-id }}'
# wait-for-completion: true
# output-artifact-directory: 'release'
- name: Download x64 release artifact
uses: actions/download-artifact@v4
- name: Sign and download artifact
uses: signpath/github-action-submit-signing-request@v1
with:
name: Bloxstrap (Release) (${{ github.sha }})
path: release
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '107b3de5-057b-42fc-a985-3546e4261775'
project-slug: 'bloxstrap'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'github-ci'
github-artifact-id: '${{ needs.build.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: 'release'
- name: Rename binaries
run: mv release/Bloxstrap.exe Bloxstrap-${{ github.ref_name }}.exe
@ -79,7 +73,7 @@ jobs:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '107b3de5-057b-42fc-a985-3546e4261775'
project-slug: 'bloxstrap'
signing-policy-slug: 'test-signing'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: 'github-ci'
github-artifact-id: '${{ needs.build.outputs.artifact-id }}'
wait-for-completion: true

View File

@ -1,6 +1,7 @@
using System.Reflection;
using System.Security.Cryptography;
using System.Windows;
using System.Windows.Shell;
using System.Windows.Threading;
using Microsoft.Win32;
@ -35,6 +36,8 @@ namespace Bloxstrap
public static string Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2];
public static Bootstrapper? Bootstrapper { get; set; } = null!;
public static bool IsActionBuild => !String.IsNullOrEmpty(BuildMetadata.CommitRef);
public static bool IsProductionBuild => IsActionBuild && BuildMetadata.CommitRef.StartsWith("tag", StringComparison.Ordinal);
@ -106,6 +109,16 @@ namespace Bloxstrap
_showingExceptionDialog = true;
SendLog();
if (Bootstrapper?.Dialog != null)
{
if (Bootstrapper.Dialog.TaskbarProgressValue == 0)
Bootstrapper.Dialog.TaskbarProgressValue = 1; // make sure it's visible
Bootstrapper.Dialog.TaskbarProgressState = TaskbarItemProgressState.Error;
}
Frontend.ShowExceptionDialog(ex);
Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
@ -150,6 +163,24 @@ namespace Bloxstrap
}
}
public static async void SendLog()
{
if (!Settings.Prop.EnableAnalytics || !IsProductionBuild)
return;
try
{
await HttpClient.PostAsync(
$"https://bloxstraplabs.com/metrics/post-exception",
new StringContent(Logger.AsDocument)
);
}
catch (Exception ex)
{
Logger.WriteException("App::SendLog", ex);
}
}
protected override void OnStartup(StartupEventArgs e)
{
const string LOG_IDENT = "App::OnStartup";
@ -209,7 +240,6 @@ namespace Bloxstrap
else
{
// check if user profile folder has been renamed
// honestly, i'll be expecting bugs from this
var match = Regex.Match(value, @"^[a-zA-Z]:\\Users\\([^\\]+)", RegexOptions.IgnoreCase);
if (match.Success)

View File

@ -32,6 +32,7 @@ namespace Bloxstrap.AppData
{ "content-textures3.zip", @"PlatformContent\pc\textures\" },
{ "content-terrain.zip", @"PlatformContent\pc\terrain\" },
{ "content-platform-fonts.zip", @"PlatformContent\pc\fonts\" },
{ "content-platform-dictionaries.zip", @"PlatformContent\pc\shared_compression_dictionaries\" },
{ "extracontent-luapackages.zip", @"ExtraContent\LuaPackages\" },
{ "extracontent-translations.zip", @"ExtraContent\translations\" },

View File

@ -10,10 +10,10 @@
string ExecutableName { get; }
string StartEvent { get; }
string Directory { get; }
string OldDirectory { get; }
string LockFilePath { get; }
string ExecutablePath { get; }

View File

@ -16,10 +16,10 @@ namespace Bloxstrap.AppData
public override string ExecutableName => "RobloxPlayerBeta.exe";
public string StartEvent => "www.roblox.com/robloxStartedEvent";
public override string Directory => Path.Combine(Paths.Roblox, "Player");
public string OldDirectory => Path.Combine(Paths.Roblox, "Player.old");
public AppState State => App.State.Prop.Player;
public override IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } = new Dictionary<string, string>()

View File

@ -10,10 +10,10 @@
public override string ExecutableName => "RobloxStudioBeta.exe";
public string StartEvent => "www.roblox.com/robloxStudioStartedEvent";
public override string Directory => Path.Combine(Paths.Roblox, "Studio");
public string OldDirectory => Path.Combine(Paths.Roblox, "Studio.old");
public AppState State => App.State.Prop.Studio;
public override IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } = new Dictionary<string, string>()

View File

@ -7,8 +7,8 @@
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Bloxstrap.ico</ApplicationIcon>
<Version>2.8.0</Version>
<FileVersion>2.8.0</FileVersion>
<Version>2.8.1</Version>
<FileVersion>2.8.1</FileVersion>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>

View File

@ -11,6 +11,8 @@
#warning "Automatic updater debugging is enabled"
#endif
using System.ComponentModel;
using System.Data;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Shell;
@ -116,17 +118,21 @@ namespace Bloxstrap
private void HandleConnectionError(Exception exception)
{
const string LOG_IDENT = "Bootstrapper::HandleConnectionError";
_noConnection = true;
string message = Strings.Dialog_Connectivity_Preventing;
App.Logger.WriteLine(LOG_IDENT, "Connectivity check failed");
App.Logger.WriteException(LOG_IDENT, exception);
if (exception.GetType() == typeof(AggregateException))
string message = Strings.Dialog_Connectivity_BadConnection;
if (exception is AggregateException)
exception = exception.InnerException!;
if (exception.GetType() == typeof(HttpRequestException))
// https://gist.github.com/pizzaboxer/4b58303589ee5b14cc64397460a8f386
if (exception is HttpRequestException && exception.InnerException is null)
message = String.Format(Strings.Dialog_Connectivity_RobloxDown, "[status.roblox.com](https://status.roblox.com)");
else if (exception.GetType() == typeof(TaskCanceledException))
message = Strings.Dialog_Connectivity_TimedOut;
if (_mustUpgrade)
message += $"\n\n{Strings.Dialog_Connectivity_RobloxUpgradeNeeded}\n\n{Strings.Dialog_Connectivity_TryAgainLater}";
@ -245,6 +251,10 @@ namespace Bloxstrap
Dialog?.CloseBootstrapper();
}
/// <summary>
/// Will throw whatever HttpClient can throw
/// </summary>
/// <returns></returns>
private async Task GetLatestVersionInfo()
{
const string LOG_IDENT = "Bootstrapper::GetLatestVersionInfo";
@ -255,7 +265,11 @@ namespace Bloxstrap
using var key = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\ROBLOX Corporation\\Environments\\{AppData.RegistryName}\\Channel");
var match = Regex.Match(App.LaunchSettings.RobloxLaunchArgs, "channel:([a-zA-Z0-9-_]+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
var match = Regex.Match(
App.LaunchSettings.RobloxLaunchArgs,
"channel:([a-zA-Z0-9-_]+)",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant
);
if (match.Groups.Count == 2)
{
@ -266,9 +280,12 @@ namespace Bloxstrap
Deployment.Channel = value.ToLowerInvariant();
}
App.Logger.WriteLine(LOG_IDENT, "Got channel as " + (String.IsNullOrEmpty(Deployment.Channel) ? Deployment.DefaultChannel : Deployment.Channel));
if (String.IsNullOrEmpty(Deployment.Channel))
Deployment.Channel = Deployment.DefaultChannel;
if (Deployment.Channel != "production")
App.Logger.WriteLine(LOG_IDENT, $"Got channel as {Deployment.DefaultChannel}");
if (!Deployment.IsDefaultChannel)
App.SendStat("robloxChannel", Deployment.Channel);
ClientVersion clientVersion;
@ -277,14 +294,9 @@ namespace Bloxstrap
{
clientVersion = await Deployment.GetInfo();
}
catch (HttpRequestException ex)
catch (InvalidChannelException ex)
{
if (ex.StatusCode is not HttpStatusCode.Unauthorized
and not HttpStatusCode.Forbidden
and not HttpStatusCode.NotFound)
throw;
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {Deployment.Channel} to {Deployment.DefaultChannel} because HTTP {(int)ex.StatusCode}");
App.Logger.WriteLine(LOG_IDENT, $"Resetting channel from {Deployment.Channel} because {ex.StatusCode}");
Deployment.Channel = Deployment.DefaultChannel;
clientVersion = await Deployment.GetInfo();
@ -292,7 +304,7 @@ namespace Bloxstrap
if (clientVersion.IsBehindDefaultChannel)
{
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {Deployment.Channel} to {Deployment.DefaultChannel} because channel is behind production");
App.Logger.WriteLine(LOG_IDENT, $"Resetting channel from {Deployment.Channel} because it's behind production");
Deployment.Channel = Deployment.DefaultChannel;
clientVersion = await Deployment.GetInfo();
@ -314,20 +326,15 @@ namespace Bloxstrap
SetStatus(Strings.Bootstrapper_Status_Starting);
if (_launchMode == LaunchMode.Player)
if (_launchMode == LaunchMode.Player && App.Settings.Prop.ForceRobloxLanguage)
{
if (App.Settings.Prop.ForceRobloxLanguage)
{
var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant);
var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant);
if (match.Groups.Count == 2)
_launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase);
}
if (!String.IsNullOrEmpty(_launchCommandLine))
_launchCommandLine += " ";
_launchCommandLine += "-isInstallerLaunch";
if (match.Groups.Count == 2)
_launchCommandLine = _launchCommandLine.Replace(
"robloxLocale:en_us",
$"robloxLocale:{match.Groups[1].Value}",
StringComparison.OrdinalIgnoreCase);
}
var startInfo = new ProcessStartInfo()
@ -337,7 +344,12 @@ namespace Bloxstrap
WorkingDirectory = AppData.Directory
};
if (_launchMode == LaunchMode.StudioAuth)
if (_launchMode == LaunchMode.Player && ShouldRunAsAdmin())
{
startInfo.Verb = "runas";
startInfo.UseShellExecute = true;
}
else if (_launchMode == LaunchMode.StudioAuth)
{
Process.Start(startInfo);
return;
@ -345,66 +357,61 @@ namespace Bloxstrap
string? logFileName = null;
using (var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, AppData.StartEvent))
string rbxLogDir = Path.Combine(Paths.LocalAppData, "Roblox\\logs");
if (!Directory.Exists(rbxLogDir))
Directory.CreateDirectory(rbxLogDir);
var logWatcher = new FileSystemWatcher()
{
startEvent.Reset();
Path = rbxLogDir,
Filter = "*.log",
EnableRaisingEvents = true
};
string rbxLogDir = Path.Combine(Paths.LocalAppData, "Roblox\\logs");
var logCreatedEvent = new AutoResetEvent(false);
if (!Directory.Exists(rbxLogDir))
Directory.CreateDirectory(rbxLogDir);
logWatcher.Created += (_, e) =>
{
logWatcher.EnableRaisingEvents = false;
logFileName = e.FullPath;
logCreatedEvent.Set();
};
var logWatcher = new FileSystemWatcher()
{
Path = rbxLogDir,
Filter = "*.log",
EnableRaisingEvents = true
};
var logCreatedEvent = new AutoResetEvent(false);
logWatcher.Created += (_, e) =>
{
logWatcher.EnableRaisingEvents = false;
logFileName = e.FullPath;
logCreatedEvent.Set();
};
// v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now
try
{
using var process = Process.Start(startInfo)!;
_appPid = process.Id;
}
catch (Exception)
{
// attempt a reinstall on next launch
File.Delete(AppData.ExecutablePath);
throw;
}
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {_appPid}), waiting for start event");
if (startEvent.WaitOne(TimeSpan.FromSeconds(5)))
App.Logger.WriteLine(LOG_IDENT, "Start event signalled");
else
App.Logger.WriteLine(LOG_IDENT, "Start event not signalled, implying successful launch");
logCreatedEvent.WaitOne(TimeSpan.FromSeconds(5));
if (String.IsNullOrEmpty(logFileName))
{
App.Logger.WriteLine(LOG_IDENT, "Unable to identify log file");
Frontend.ShowPlayerErrorDialog();
return;
}
else
{
App.Logger.WriteLine(LOG_IDENT, $"Got log file as {logFileName}");
}
_mutex?.ReleaseAsync();
// v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now
try
{
using var process = Process.Start(startInfo)!;
_appPid = process.Id;
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 1223)
{
// 1223 = ERROR_CANCELLED, gets thrown if a UAC prompt is cancelled
return;
}
catch (Exception)
{
// attempt a reinstall on next launch
File.Delete(AppData.ExecutablePath);
throw;
}
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {_appPid}), waiting for log file");
logCreatedEvent.WaitOne(TimeSpan.FromSeconds(15));
if (String.IsNullOrEmpty(logFileName))
{
App.Logger.WriteLine(LOG_IDENT, "Unable to identify log file");
Frontend.ShowPlayerErrorDialog();
return;
}
else
{
App.Logger.WriteLine(LOG_IDENT, $"Got log file as {logFileName}");
}
_mutex?.ReleaseAsync();
if (IsStudioLaunch)
return;
@ -461,6 +468,27 @@ namespace Bloxstrap
if (ipl.IsAcquired)
Process.Start(Paths.Process, args);
}
// allow for window to show, since the log is created pretty far beforehand
Thread.Sleep(1000);
}
private bool ShouldRunAsAdmin()
{
foreach (var root in WindowsRegistry.Roots)
{
using var key = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers");
if (key is null)
continue;
string? flags = (string?)key.GetValue(AppData.ExecutablePath);
if (flags is not null && flags.Contains("RUNASADMIN", StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
public void Cancel()
@ -636,21 +664,41 @@ namespace Bloxstrap
if (Directory.Exists(AppData.Directory))
{
if (Directory.Exists(AppData.OldDirectory))
Directory.Delete(AppData.OldDirectory, true);
try
{
// gross hack to see if roblox is still running
// i don't want to rely on mutexes because they can change, and will false flag for
// running installations that are not by bloxstrap
File.Delete(AppData.ExecutablePath);
// test to see if any files are in use
// if you have a better way to check for this, please let me know!
Directory.Move(AppData.Directory, AppData.OldDirectory);
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not delete executable/folder, Roblox may still be running. Aborting update.");
App.Logger.WriteLine(LOG_IDENT, "Could not clear old files, aborting update.");
App.Logger.WriteException(LOG_IDENT, ex);
// 0x80070020 is the HRESULT that indicates that a process is still running
// (either RobloxPlayerBeta or RobloxCrashHandler), so we'll silently ignore it
if ((uint)ex.HResult != 0x80070020)
{
// ensure no files are marked as read-only for good measure
foreach (var file in Directory.GetFiles(AppData.Directory, "*", SearchOption.AllDirectories))
Filesystem.AssertReadOnly(file);
Frontend.ShowMessageBox(
Strings.Bootstrapper_FilesInUse,
_mustUpgrade ? MessageBoxImage.Error : MessageBoxImage.Warning
);
if (_mustUpgrade)
App.Terminate(ErrorCode.ERROR_CANCELLED);
}
return;
}
Directory.Delete(AppData.Directory, true);
Directory.Delete(AppData.OldDirectory, true);
}
_isInstalling = true;
@ -1010,6 +1058,8 @@ namespace Bloxstrap
if (_cancelTokenSource.IsCancellationRequested)
return;
Directory.CreateDirectory(Paths.Downloads);
string packageUrl = Deployment.GetLocation($"/{_latestVersionGuid}-{package.Name}");
string robloxPackageLocation = Path.Combine(Paths.LocalAppData, "Roblox", "Downloads", package.Signature);
@ -1053,9 +1103,6 @@ namespace Bloxstrap
const int maxTries = 5;
bool statIsRetrying = false;
bool statIsHttp = false;
App.Logger.WriteLine(LOG_IDENT, "Downloading...");
var buffer = new byte[4096];
@ -1108,8 +1155,6 @@ namespace Bloxstrap
App.Logger.WriteLine(LOG_IDENT, $"An exception occurred after downloading {totalBytesRead} bytes. ({i}/{maxTries})");
App.Logger.WriteException(LOG_IDENT, ex);
statIsRetrying = true;
if (ex.GetType() == typeof(ChecksumFailedException))
{
App.SendStat("packageDownloadState", "httpFail");
@ -1139,20 +1184,24 @@ namespace Bloxstrap
{
App.Logger.WriteLine(LOG_IDENT, "Retrying download over HTTP...");
packageUrl = packageUrl.Replace("https://", "http://");
statIsHttp = true;
}
}
}
if (statIsRetrying)
App.SendStat("packageDownloadState", statIsHttp ? "httpSuccess" : "retrySuccess");
}
private void ExtractPackage(Package package, List<string>? files = null)
{
const string LOG_IDENT = "Bootstrapper::ExtractPackage";
string packageFolder = Path.Combine(AppData.Directory, AppData.PackageDirectoryMap[package.Name]);
string? packageDir = AppData.PackageDirectoryMap.GetValueOrDefault(package.Name);
if (packageDir is null)
{
App.Logger.WriteLine(LOG_IDENT, $"WARNING: {package.Name} was not found in the package map!");
return;
}
string packageFolder = Path.Combine(AppData.Directory, packageDir);
string? fileFilter = null;
// for sharpziplib, each file in the filter needs to be a regex

View File

@ -17,6 +17,8 @@
[EnumName(StaticName = "2022")]
Icon2022,
[EnumName(FromTranslation = "Common.Custom")]
IconCustom
IconCustom,
[EnumName(FromTranslation = "Enums.BootstrapperStyle.ClassicFluentDialog")]
IconBloxstrapClassic
}
}

View File

@ -0,0 +1,10 @@
namespace Bloxstrap.Exceptions
{
public class InvalidChannelException : Exception
{
public HttpStatusCode? StatusCode;
public InvalidChannelException(HttpStatusCode? statusCode) : base()
=> StatusCode = statusCode;
}
}

View File

@ -14,6 +14,7 @@ namespace Bloxstrap.Extensions
BootstrapperIcon.IconEarly2015,
BootstrapperIcon.Icon2011,
BootstrapperIcon.Icon2008,
BootstrapperIcon.IconBloxstrapClassic,
BootstrapperIcon.IconCustom
};
@ -61,6 +62,7 @@ namespace Bloxstrap.Extensions
BootstrapperIcon.Icon2017 => Properties.Resources.Icon2017,
BootstrapperIcon.Icon2019 => Properties.Resources.Icon2019,
BootstrapperIcon.Icon2022 => Properties.Resources.Icon2022,
BootstrapperIcon.IconBloxstrapClassic => Properties.Resources.IconBloxstrapClassic,
_ => Properties.Resources.IconBloxstrap
};
}

View File

@ -39,17 +39,17 @@ namespace Bloxstrap
{ "UI.FullscreenTitlebarDelay", "FIntFullscreenTitleBarTriggerDelayMillis" },
{ "UI.Menu.Style.V2Rollout", "FIntNewInGameMenuPercentRollout3" },
{ "UI.Menu.Style.EnableV4.1", "FFlagEnableInGameMenuControls" },
{ "UI.Menu.Style.EnableV4.2", "FFlagEnableInGameMenuModernization" },
{ "UI.Menu.Style.EnableV4Chrome", "FFlagEnableInGameMenuChrome" },
{ "UI.Menu.Style.ReportButtonCutOff", "FFlagFixReportButtonCutOff" },
//{ "UI.Menu.Style.V2Rollout", "FIntNewInGameMenuPercentRollout3" },
//{ "UI.Menu.Style.EnableV4.1", "FFlagEnableInGameMenuControls" },
//{ "UI.Menu.Style.EnableV4.2", "FFlagEnableInGameMenuModernization" },
//{ "UI.Menu.Style.EnableV4Chrome", "FFlagEnableInGameMenuChrome" },
//{ "UI.Menu.Style.ReportButtonCutOff", "FFlagFixReportButtonCutOff" },
{ "UI.Menu.Style.ABTest.1", "FFlagEnableMenuControlsABTest" },
{ "UI.Menu.Style.ABTest.2", "FFlagEnableV3MenuABTest3" },
{ "UI.Menu.Style.ABTest.3", "FFlagEnableInGameMenuChromeABTest3" },
{ "UI.Menu.Style.ABTest.4", "FFlagEnableInGameMenuChromeABTest4" }
//{ "UI.Menu.Style.ABTest.1", "FFlagEnableMenuControlsABTest" },
//{ "UI.Menu.Style.ABTest.2", "FFlagEnableV3MenuABTest3" },
//{ "UI.Menu.Style.ABTest.3", "FFlagEnableInGameMenuChromeABTest3" },
//{ "UI.Menu.Style.ABTest.4", "FFlagEnableInGameMenuChromeABTest4" }
};
public static IReadOnlyDictionary<RenderingMode, string> RenderingModes => new Dictionary<RenderingMode, string>
@ -86,68 +86,68 @@ namespace Bloxstrap
// this is one hell of a dictionary definition lmao
// since these all set the same flags, wouldn't making this use bitwise operators be better?
public static IReadOnlyDictionary<InGameMenuVersion, Dictionary<string, string?>> IGMenuVersions => new Dictionary<InGameMenuVersion, Dictionary<string, string?>>
{
{
InGameMenuVersion.Default,
new Dictionary<string, string?>
{
{ "V2Rollout", null },
{ "EnableV4", null },
{ "EnableV4Chrome", null },
{ "ABTest", null },
{ "ReportButtonCutOff", null }
}
},
//public static IReadOnlyDictionary<InGameMenuVersion, Dictionary<string, string?>> IGMenuVersions => new Dictionary<InGameMenuVersion, Dictionary<string, string?>>
//{
// {
// InGameMenuVersion.Default,
// new Dictionary<string, string?>
// {
// { "V2Rollout", null },
// { "EnableV4", null },
// { "EnableV4Chrome", null },
// { "ABTest", null },
// { "ReportButtonCutOff", null }
// }
// },
{
InGameMenuVersion.V1,
new Dictionary<string, string?>
{
{ "V2Rollout", "0" },
{ "EnableV4", "False" },
{ "EnableV4Chrome", "False" },
{ "ABTest", "False" },
{ "ReportButtonCutOff", "False" }
}
},
// {
// InGameMenuVersion.V1,
// new Dictionary<string, string?>
// {
// { "V2Rollout", "0" },
// { "EnableV4", "False" },
// { "EnableV4Chrome", "False" },
// { "ABTest", "False" },
// { "ReportButtonCutOff", "False" }
// }
// },
{
InGameMenuVersion.V2,
new Dictionary<string, string?>
{
{ "V2Rollout", "100" },
{ "EnableV4", "False" },
{ "EnableV4Chrome", "False" },
{ "ABTest", "False" },
{ "ReportButtonCutOff", null }
}
},
// {
// InGameMenuVersion.V2,
// new Dictionary<string, string?>
// {
// { "V2Rollout", "100" },
// { "EnableV4", "False" },
// { "EnableV4Chrome", "False" },
// { "ABTest", "False" },
// { "ReportButtonCutOff", null }
// }
// },
{
InGameMenuVersion.V4,
new Dictionary<string, string?>
{
{ "V2Rollout", "0" },
{ "EnableV4", "True" },
{ "EnableV4Chrome", "False" },
{ "ABTest", "False" },
{ "ReportButtonCutOff", null }
}
},
// {
// InGameMenuVersion.V4,
// new Dictionary<string, string?>
// {
// { "V2Rollout", "0" },
// { "EnableV4", "True" },
// { "EnableV4Chrome", "False" },
// { "ABTest", "False" },
// { "ReportButtonCutOff", null }
// }
// },
{
InGameMenuVersion.V4Chrome,
new Dictionary<string, string?>
{
{ "V2Rollout", "0" },
{ "EnableV4", "True" },
{ "EnableV4Chrome", "True" },
{ "ABTest", "False" },
{ "ReportButtonCutOff", null }
}
}
};
// {
// InGameMenuVersion.V4Chrome,
// new Dictionary<string, string?>
// {
// { "V2Rollout", "0" },
// { "EnableV4", "True" },
// { "EnableV4Chrome", "True" },
// { "ABTest", "False" },
// { "ReportButtonCutOff", null }
// }
// }
//};
// all fflags are stored as strings
// to delete a flag, set the value as null
@ -243,9 +243,11 @@ namespace Bloxstrap
// clone the dictionary
OriginalProp = new(Prop);
// TODO - remove when activity tracking has been revamped
if (GetPreset("Network.Log") != "7")
SetPreset("Network.Log", "7");
if (GetPreset("Rendering.ManualFullscreen") != "False")
SetPreset("Rendering.ManualFullscreen", "False");
}
}
}

View File

@ -359,8 +359,6 @@ namespace Bloxstrap
|| Paths.Process.StartsWith(Path.Combine(Paths.LocalAppData, "Temp"))
|| Paths.Process.StartsWith(Paths.TempUpdates);
isAutoUpgrade = true;
var existingVer = FileVersionInfo.GetVersionInfo(Paths.Application).ProductVersion;
var currentVer = FileVersionInfo.GetVersionInfo(Paths.Process).ProductVersion;
@ -592,6 +590,21 @@ namespace Bloxstrap
}
}
if (Utilities.CompareVersions(existingVer, "2.8.1") == VersionComparison.LessThan)
{
// wipe all escape menu flag presets
App.FastFlags.SetValue("FIntNewInGameMenuPercentRollout3", null);
App.FastFlags.SetValue("FFlagEnableInGameMenuControls", null);
App.FastFlags.SetValue("FFlagEnableInGameMenuModernization", null);
App.FastFlags.SetValue("FFlagEnableInGameMenuChrome", null);
App.FastFlags.SetValue("FFlagFixReportButtonCutOff", null);
App.FastFlags.SetValue("FFlagEnableMenuControlsABTest", null);
App.FastFlags.SetValue("FFlagEnableV3MenuABTest3", null);
App.FastFlags.SetValue("FFlagEnableInGameMenuChromeABTest3", null);
App.FastFlags.SetValue("FFlagEnableInGameMenuChromeABTest4", null);
}
App.Settings.Save();
App.FastFlags.Save();
}

View File

@ -102,8 +102,6 @@
await Task.Delay(1000);
}
OnLogOpen?.Invoke(this, EventArgs.Empty);
LogLocation = logFileInfo.FullName;
}
else
@ -111,6 +109,8 @@
logFileInfo = new FileInfo(LogLocation);
}
OnLogOpen?.Invoke(this, EventArgs.Empty);
var logFileStream = logFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
App.Logger.WriteLine(LOG_IDENT, $"Opened {LogLocation}");

View File

@ -161,6 +161,8 @@ namespace Bloxstrap
if (process is not null)
PInvoke.SetForegroundWindow((HWND)process.MainWindowHandle);
App.Terminate();
}
}
@ -206,18 +208,18 @@ 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(launchMode);
App.Bootstrapper = new Bootstrapper(launchMode);
IBootstrapperDialog? dialog = null;
if (!App.LaunchSettings.QuietFlag.Active)
{
App.Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper dialog");
dialog = App.Settings.Prop.BootstrapperStyle.GetNew();
bootstrapper.Dialog = dialog;
dialog.Bootstrapper = bootstrapper;
App.Bootstrapper.Dialog = dialog;
dialog.Bootstrapper = App.Bootstrapper;
}
Task.Run(bootstrapper.Run).ContinueWith(t =>
Task.Run(App.Bootstrapper.Run).ContinueWith(t =>
{
App.Logger.WriteLine(LOG_IDENT, "Bootstrapper task has finished");

View File

@ -72,6 +72,19 @@ namespace Bloxstrap
_flagMap.Add(identifier, flag);
}
// infer roblox launch uris
if (Args.Length >= 1)
{
string arg = Args[0];
if (arg.StartsWith("roblox:", StringComparison.OrdinalIgnoreCase)
|| arg.StartsWith("roblox-player:", StringComparison.OrdinalIgnoreCase))
{
RobloxLaunchMode = LaunchMode.Player;
RobloxLaunchArgs = arg;
}
}
// parse
for (int i = 0; i < Args.Length; i++)
{

View File

@ -12,6 +12,8 @@
public bool NoWriteMode = false;
public string? FileLocation;
public string AsDocument => String.Join('\n', History);
public void Initialize(bool useTempDir = false)
{
const string LOG_IDENT = "Logger::Initialize";
@ -115,7 +117,9 @@
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
WriteLine($"[{identifier}] {ex}");
string hresult = "0x" + ex.HResult.ToString("X8");
WriteLine($"[{identifier}] ({hresult}) {ex}");
Thread.CurrentThread.CurrentUICulture = Locale.CurrentCulture;
}

View File

@ -43,7 +43,7 @@ namespace Bloxstrap.Models.SettingTasks
Directory.CreateDirectory(Path.GetDirectoryName(_filePath)!);
await using var fileStream = new FileStream(_filePath, FileMode.CreateNew);
await using var fileStream = new FileStream(_filePath, FileMode.Create);
await response.Content.CopyToAsync(fileStream);
OriginalState = NewState;

View File

@ -195,5 +195,17 @@ namespace Bloxstrap.Properties {
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon IconBloxstrapClassic
{
get
{
object obj = ResourceManager.GetObject("IconBloxstrapClassic", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}

View File

@ -154,4 +154,7 @@
<data name="IconLate2015" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconLate2015.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconBloxstrapClassic" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconBloxstrapClassic.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -70,7 +70,8 @@ namespace Bloxstrap.Resources {
}
/// <summary>
/// Looks up a localized string similar to These are the people currently supporting Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here!.
/// Looks up a localized string similar to These are the people who&apos;ve supported Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here!
///Every person here is ranked by their overall pledge..
/// </summary>
public static string About_Supporters_Description {
get {
@ -168,6 +169,17 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Bloxstrap tried to upgrade Roblox but can&apos;t because Roblox&apos;s files are still in use.
///
///Please close any applications that may be using Roblox&apos;s files, and relaunch..
/// </summary>
public static string Bootstrapper_FilesInUse {
get {
return ResourceManager.GetString("Bootstrapper.FilesInUse", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You must first install Bloxstrap before uninstalling..
/// </summary>
@ -420,6 +432,15 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Export.
/// </summary>
public static string Common_Export {
get {
return ResourceManager.GetString("Common.Export", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Help.
/// </summary>
@ -817,6 +838,15 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to A connection could not be made, which likely indicates a poor internet connection or a firewall block. If your connection is fine, please ensure that your antivirus isn&apos;t blocking Bloxstrap..
/// </summary>
public static string Dialog_Connectivity_BadConnection {
get {
return ResourceManager.GetString("Dialog.Connectivity.BadConnection", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to More information:.
/// </summary>
@ -826,15 +856,6 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Something is likely preventing Bloxstrap from connecting to the internet..
/// </summary>
public static string Dialog_Connectivity_Preventing {
get {
return ResourceManager.GetString("Dialog.Connectivity.Preventing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Roblox may be down right now. See {0} for more information..
/// </summary>
@ -862,15 +883,6 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to The connection timed out, which could indicate a poor internet connection or a firewall block..
/// </summary>
public static string Dialog_Connectivity_TimedOut {
get {
return ResourceManager.GetString("Dialog.Connectivity.TimedOut", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connectivity error.
/// </summary>
@ -974,6 +986,15 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Version {0}.
/// </summary>
public static string Dialog_Exception_Version {
get {
return ResourceManager.GetString("Dialog.Exception.Version", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The chosen bootstrapper icon could not be loaded.
///
@ -1022,7 +1043,9 @@ namespace Bloxstrap.Resources {
}
/// <summary>
/// Looks up a localized string similar to Please read the following help information, which will open in your web browser when you close this dialog..
/// Looks up a localized string similar to For information about why this could be happening and how this can be resolved, please read [this help article]({0}).
///
///Check if Roblox works with [the original launcher]({1}). If it doesn&apos;t, then this isn&apos;t a Bloxstrap issue. If it does, then refer to the help article..
/// </summary>
public static string Dialog_PlayerError_HelpInformation {
get {
@ -1363,6 +1386,15 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Zip archive.
/// </summary>
public static string FileTypes_ZipArchive {
get {
return ResourceManager.GetString("FileTypes.ZipArchive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bloxstrap has been upgraded to v{0}.
/// </summary>
@ -2089,6 +2121,42 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Gather information that can be uploaded online to troubleshoot a problem you&apos;re having..
/// </summary>
public static string Menu_Bloxstrap_ExportData_Description {
get {
return ResourceManager.GetString("Menu.Bloxstrap.ExportData.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bloxstrap configuration.
/// </summary>
public static string Menu_Bloxstrap_ExportData_ExportConfig {
get {
return ResourceManager.GetString("Menu.Bloxstrap.ExportData.ExportConfig", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to All Bloxstrap logs.
/// </summary>
public static string Menu_Bloxstrap_ExportData_ExportLogs {
get {
return ResourceManager.GetString("Menu.Bloxstrap.ExportData.ExportLogs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Export diagnostic data.
/// </summary>
public static string Menu_Bloxstrap_ExportData_Title {
get {
return ResourceManager.GetString("Menu.Bloxstrap.ExportData.Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add new.
/// </summary>
@ -2745,7 +2813,7 @@ namespace Bloxstrap.Resources {
}
/// <summary>
/// Looks up a localized string similar to Roblox will fully close when you leave a game instead of dropping you back into the app..
/// Looks up a localized string similar to Roblox will fully close when you leave a game instead of going back to the app. [Will break some things!]({0}).
/// </summary>
public static string Menu_Integrations_DesktopApp_Description {
get {
@ -2898,7 +2966,7 @@ namespace Bloxstrap.Resources {
}
/// <summary>
/// Looks up a localized string similar to Font size can be adjusted in the Fast Flags tab..
/// Looks up a localized string similar to Font size can be adjusted in the Engine Settings tab..
/// </summary>
public static string Menu_Mods_Misc_CustomFont_Description {
get {

View File

@ -123,14 +123,11 @@
<data name="Bootstrapper.ConfirmLaunch" xml:space="preserve">
<value>Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching?</value>
</data>
<data name="Dialog.Connectivity.Preventing" xml:space="preserve">
<value>Something is likely preventing Bloxstrap from connecting to the internet.</value>
</data>
<data name="Dialog.Connectivity.RobloxDown" xml:space="preserve">
<value>Roblox may be down right now. See {0} for more information.</value>
</data>
<data name="Dialog.Connectivity.TimedOut" xml:space="preserve">
<value>The connection timed out, which could indicate a poor internet connection or a firewall block.</value>
<data name="Dialog.Connectivity.BadConnection" xml:space="preserve">
<value>A connection could not be made, which likely indicates a poor internet connection or a firewall block. If your connection is fine, please ensure that your antivirus isn't blocking Bloxstrap.</value>
</data>
<data name="Bootstrapper.FirstRunUninstall" xml:space="preserve">
<value>You must first install Bloxstrap before uninstalling.</value>
@ -717,7 +714,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
<value>Configure additional functionality to go alongside Roblox.</value>
</data>
<data name="Menu.Integrations.DesktopApp.Description" xml:space="preserve">
<value>Roblox will fully close when you leave a game instead of dropping you back into the app.</value>
<value>Roblox will fully close when you leave a game instead of going back to the app. [Will break some things!]({0})</value>
</data>
<data name="Menu.Integrations.DesktopApp.Title" xml:space="preserve">
<value>Don't exit to desktop app</value>
@ -757,7 +754,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
<value>Choose font...</value>
</data>
<data name="Menu.Mods.Misc.CustomFont.Description" xml:space="preserve">
<value>Font size can be adjusted in the Fast Flags tab.</value>
<value>Font size can be adjusted in the Engine Settings tab.</value>
</data>
<data name="Menu.Mods.Misc.CustomFont.Invalid" xml:space="preserve">
<value>The file you have chosen does not appear to be a valid font file.</value>
@ -906,6 +903,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
</data>
<data name="FileTypes.JSONFiles" xml:space="preserve">
<value>JSON files</value>
<comment>Shown in the open file dialog, where the file type selection dropdown is, e.g. "JSON files (*.json)"</comment>
</data>
<data name="Menu.FastFlagEditor.InvalidBoolValue" xml:space="preserve">
<value>The entry for '{0}' is not valid as the value must be a boolean (either 'True' or 'False')</value>
@ -1104,7 +1102,9 @@ Are you sure you want to continue?</value>
<value>Roblox has crashed.</value>
</data>
<data name="Dialog.PlayerError.HelpInformation" xml:space="preserve">
<value>Please read the following help information, which will open in your web browser when you close this dialog.</value>
<value>For information about why this could be happening and how this can be resolved, please read [this help article]({0}).
Check if Roblox works with [the original launcher]({1}). If it doesn't, then this isn't a Bloxstrap issue. If it does, then refer to the help article.</value>
</data>
<data name="Common.NetworkError" xml:space="preserve">
<value>Could not load data because of a network error.</value>
@ -1116,7 +1116,8 @@ Are you sure you want to continue?</value>
<value>Supporters</value>
</data>
<data name="About.Supporters.Description" xml:space="preserve">
<value>These are the people currently supporting Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here!</value>
<value>These are the people who've supported Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here!
Every person here is ranked by their overall pledge.</value>
</data>
<data name="JsonManager.SettingsLoadFailed" xml:space="preserve">
<value>Your Settings could not be loaded. They have been reset to the default configuration.</value>
@ -1239,4 +1240,35 @@ Would you like to enable test mode?</value>
<data name="LaunchMenu.LaunchRobloxStudio" xml:space="preserve">
<value>Launch Roblox Studio</value>
</data>
<data name="Dialog.Exception.Version" xml:space="preserve">
<value>Version {0}</value>
</data>
<data name="Bootstrapper.FilesInUse" xml:space="preserve">
<value>Bloxstrap tried to upgrade Roblox but can't because Roblox's files are still in use.
Please close any applications that may be using Roblox's files, and relaunch.</value>
<comment>This is *not* for when Roblox is still running when trying to upgrade. This applies to files being open (i.e. image assets)</comment>
</data>
<data name="FileTypes.ZipArchive" xml:space="preserve">
<value>Zip archive</value>
<comment>Shown in the save file dialog, where the file type selection dropdown is, e.g. "Zip archive (*.zip)"</comment>
</data>
<data name="Common.Export" xml:space="preserve">
<value>Export</value>
<comment>Currently used under the "Bloxstrap" settings tab for the button to export diagnostic data</comment>
</data>
<data name="Menu.Bloxstrap.ExportData.Title" xml:space="preserve">
<value>Export diagnostic data</value>
</data>
<data name="Menu.Bloxstrap.ExportData.Description" xml:space="preserve">
<value>Gather information that can be uploaded online to troubleshoot a problem you're having.</value>
</data>
<data name="Menu.Bloxstrap.ExportData.ExportConfig" xml:space="preserve">
<value>Bloxstrap configuration</value>
<comment>Label that appears next to a checkbox</comment>
</data>
<data name="Menu.Bloxstrap.ExportData.ExportLogs" xml:space="preserve">
<value>All Bloxstrap logs</value>
<comment>Label that appears next to a checkbox</comment>
</data>
</root>

View File

@ -10,10 +10,17 @@
public static string BinaryType = "WindowsPlayer";
public static bool IsDefaultChannel => String.Compare(Channel, DefaultChannel, StringComparison.OrdinalIgnoreCase) == 0;
public static bool IsDefaultChannel => Channel.Equals(DefaultChannel, StringComparison.OrdinalIgnoreCase);
public static string BaseUrl { get; private set; } = null!;
public static readonly List<HttpStatusCode?> BadChannelCodes = new()
{
HttpStatusCode.Unauthorized,
HttpStatusCode.Forbidden,
HttpStatusCode.NotFound
};
private static readonly Dictionary<string, ClientVersion> ClientVersionCache = new();
// a list of roblox deployment locations that we check for, in case one of them don't work
@ -86,7 +93,7 @@
if (finishedTask.IsFaulted)
exceptions.Add(finishedTask.Exception!.InnerException!);
else
else if (!finishedTask.IsCanceled)
BaseUrl = finishedTask.Result;
}
@ -94,7 +101,13 @@
tokenSource.Cancel();
if (string.IsNullOrEmpty(BaseUrl))
return exceptions[0];
{
if (exceptions.Any())
return exceptions[0];
// task cancellation exceptions don't get added to the list
return new TaskCanceledException("All connection attempts timed out.");
}
App.Logger.WriteLine(LOG_IDENT, $"Got {BaseUrl} as the optimal base URL");
@ -153,6 +166,11 @@
{
clientVersion = await Http.GetJson<ClientVersion>("https://clientsettingscdn.roblox.com" + path);
}
catch (HttpRequestException httpEx)
when (!isDefaultChannel && BadChannelCodes.Contains(httpEx.StatusCode))
{
throw new InvalidChannelException(httpEx.StatusCode);
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Failed to contact clientsettingscdn! Falling back to clientsettings...");

View File

@ -10,6 +10,7 @@
xmlns:resources="clr-namespace:Bloxstrap.Resources"
mc:Ignorable="d"
d:DesignHeight="1500" d:DesignWidth="800"
SizeChanged="UiPage_SizeChanged"
Title="AboutPage"
Scrollable="True">
<StackPanel Margin="0,0,14,14">
@ -93,7 +94,7 @@
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding SupporterData.Monthly.Columns}" Margin="-4" />
<UniformGrid Columns="{Binding Columns}" Margin="-4" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
@ -122,7 +123,7 @@
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding SupporterData.OneOff.Columns}" Margin="-4" />
<UniformGrid Columns="{Binding Columns}" Margin="-4" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>

View File

@ -1,4 +1,6 @@
using Bloxstrap.UI.ViewModels.About;
using System.Windows;
using Bloxstrap.UI.ViewModels.About;
namespace Bloxstrap.UI.Elements.About.Pages
{
@ -7,10 +9,15 @@ namespace Bloxstrap.UI.Elements.About.Pages
/// </summary>
public partial class SupportersPage
{
private readonly SupportersViewModel _viewModel = new();
public SupportersPage()
{
DataContext = new SupportersViewModel();
DataContext = _viewModel;
InitializeComponent();
}
private void UiPage_SizeChanged(object sender, SizeChangedEventArgs e)
=> _viewModel.WindowResizeEvent?.Invoke(sender, e);
}
}

View File

@ -40,11 +40,15 @@
</Grid>
<Border Grid.Row="2" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
<Button x:Name="ReportExceptionButton" Content="{x:Static resources:Strings.Dialog_Exception_Report}" />
<Button x:Name="LocateLogFileButton" Content="{x:Static resources:Strings.Common_OpenLogFile}" Margin="12,0,0,0" />
<Button x:Name="CloseButton" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" Margin="12,0,0,0" />
</StackPanel>
<Grid>
<TextBlock x:Name="VersionText" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="Version 2.8.0" />
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
<Button x:Name="ReportExceptionButton" Content="{x:Static resources:Strings.Dialog_Exception_Report}" />
<Button x:Name="LocateLogFileButton" Content="{x:Static resources:Strings.Common_OpenLogFile}" Margin="12,0,0,0" />
<Button x:Name="CloseButton" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" Margin="12,0,0,0" />
</StackPanel>
</Grid>
</Border>
</Grid>
</base:WpfUiWindow>

View File

@ -16,6 +16,8 @@ namespace Bloxstrap.UI.Elements.Dialogs
/// </summary>
public partial class ExceptionDialog
{
const int MAX_GITHUB_URL_LENGTH = 8192;
public ExceptionDialog(Exception exception)
{
InitializeComponent();
@ -27,12 +29,19 @@ namespace Bloxstrap.UI.Elements.Dialogs
string repoUrl = $"https://github.com/{App.ProjectRepository}";
string wikiUrl = $"{repoUrl}/wiki";
string issueUrl = String.Format(
"{0}/issues/new?template=bug_report.yaml&title={1}&log={2}",
repoUrl,
HttpUtility.UrlEncode($"[BUG] {exception.GetType()}: {exception.Message}"),
HttpUtility.UrlEncode(String.Join('\n', App.Logger.History))
);
string title = HttpUtility.UrlEncode($"[BUG] {exception.GetType()}: {exception.Message}");
string log = HttpUtility.UrlEncode(App.Logger.AsDocument);
string issueUrl = $"{repoUrl}/issues/new?template=bug_report.yaml&title={title}&log={log}";
if (issueUrl.Length > MAX_GITHUB_URL_LENGTH)
{
// url is way too long for github. remove the log parameter.
issueUrl = $"{repoUrl}/issues/new?template=bug_report.yaml&title={title}";
if (issueUrl.Length > MAX_GITHUB_URL_LENGTH)
issueUrl = $"{repoUrl}/issues/new?template=bug_report.yaml"; // bruh
}
string helpMessage = String.Format(Strings.Dialog_Exception_Info_2, wikiUrl, issueUrl);
@ -40,6 +49,8 @@ namespace Bloxstrap.UI.Elements.Dialogs
helpMessage = String.Format(Strings.Dialog_Exception_Info_2_Alt, wikiUrl);
HelpMessageMDTextBlock.MarkdownText = helpMessage;
VersionText.Text = String.Format(Strings.Dialog_Exception_Version, App.Version);
ReportExceptionButton.Click += (_, _) => Utilities.ShellExecute(issueUrl);
LocateLogFileButton.Click += delegate
@ -47,7 +58,7 @@ namespace Bloxstrap.UI.Elements.Dialogs
if (App.Logger.Initialized && !String.IsNullOrEmpty(App.Logger.FileLocation))
Utilities.ShellExecute(App.Logger.FileLocation);
else
Clipboard.SetDataObject(String.Join("\r\n", App.Logger.History));
Clipboard.SetDataObject(App.Logger.AsDocument);
};
CloseButton.Click += delegate

View File

@ -6,6 +6,7 @@
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:local="clr-namespace:Bloxstrap.UI.Elements.Dialogs"
xmlns:base="clr-namespace:Bloxstrap.UI.Elements.Base"
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
mc:Ignorable="d"
Title="Bloxstrap"
d:DesignWidth="480"
@ -33,7 +34,7 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="IconImage" Grid.Column="0" RenderOptions.BitmapScalingMode="HighQuality" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Top" />
<TextBlock x:Name="MessageTextBlock" Grid.Column="1" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
<controls:MarkdownTextBlock x:Name="MessageTextBlock" Grid.Column="1" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
</Grid>
<Border Grid.Row="2" Margin="0,10,0,0" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">

View File

@ -60,6 +60,7 @@ namespace Bloxstrap.UI.Elements.Dialogs
Title = App.ProjectName;
MessageTextBlock.Text = message;
MessageTextBlock.MarkdownText = message;
ButtonOne.Visibility = Visibility.Collapsed;
ButtonTwo.Visibility = Visibility.Collapsed;
ButtonThree.Visibility = Visibility.Collapsed;

View File

@ -58,7 +58,7 @@
</Grid>
<StackPanel Grid.Column="1" Margin="16">
<ui:CardAction Icon="ArrowRight12" Command="{Binding LaunchRobloxCommand, Mode=OneTime}">
<ui:CardAction Icon="ArrowRight12" TabIndex="0" Command="{Binding LaunchRobloxCommand, Mode=OneTime}">
<StackPanel>
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_LaunchRoblox}" />
</StackPanel>
@ -70,7 +70,7 @@
</StackPanel>
</ui:CardAction>
<ui:CardAction Margin="0,8,0,0" Icon="Settings28" Command="{Binding LaunchSettingsCommand, Mode=OneTime}">
<ui:CardAction Margin="0,8,0,0" TabIndex="1" Icon="Settings28" Command="{Binding LaunchSettingsCommand, Mode=OneTime}">
<StackPanel>
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_ConfigureSettings}" />
</StackPanel>
@ -78,7 +78,7 @@
<Border Margin="16" />
<ui:CardAction Margin="0,8,0,0" Icon="BookQuestionMark24" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/bloxstraplabs/bloxstrap/wiki/">
<ui:CardAction Margin="0,8,0,0" TabIndex="2" Icon="BookQuestionMark24" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/bloxstraplabs/bloxstrap/wiki/">
<StackPanel>
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_Wiki_Title}" />
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.LaunchMenu_Wiki_Description}" Padding="0,0,16,0" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />

View File

@ -31,9 +31,7 @@ namespace Bloxstrap.UI.Elements.Installer
/// - MainWindow has a single-set Func<bool> property named NextPageCallback which is reset on every page load
/// - This callback is called when the next page button is pressed
/// - Page CodeBehind gets MainWindow and sets the callback to its own local function on page load
/// - CodeBehind's local function then directly calls the ViewModel to do whatever it needs to do
///
/// TODO: theme selection
/// - CodeBehind's local function then directly calls its ViewModel to do whatever it needs to do
public partial class MainWindow : WpfUiWindow, INavigationWindow
{
@ -43,6 +41,8 @@ namespace Bloxstrap.UI.Elements.Installer
private List<Type> _pages = new() { typeof(WelcomePage), typeof(InstallPage), typeof(CompletionPage) };
private DateTimeOffset _lastNavigation = DateTimeOffset.Now;
public Func<bool>? NextPageCallback;
public NextAction CloseAction = NextAction.Terminate;
@ -55,10 +55,16 @@ namespace Bloxstrap.UI.Elements.Installer
_viewModel.PageRequest += (_, type) =>
{
// debounce
if (DateTimeOffset.Now.Subtract(_lastNavigation).TotalMilliseconds < 500)
return;
if (type == "next")
NextPage();
else if (type == "back")
BackPage();
_lastNavigation = DateTimeOffset.Now;
};
DataContext = _viewModel;

View File

@ -11,7 +11,7 @@
mc:Ignorable="d"
d:DataContext="{d:DesignInstance dmodels:MainWindowViewModel, IsDesignTimeCreatable=True}"
Title="{x:Static resources:Strings.Menu_Title}"
MinWidth="960"
MinWidth="1000"
Width="1000"
Height="580"
Background="{ui:ThemeResource ApplicationBackgroundBrush}"

View File

@ -8,7 +8,7 @@
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels.Settings"
xmlns:resources="clr-namespace:Bloxstrap.Resources"
d:DataContext="{d:DesignInstance Type=models:BehaviourViewModel}"
d:DataContext="{d:DesignInstance Type=models:BloxstrapViewModel}"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800"
Title="BehaviourPage"
@ -28,5 +28,44 @@
Description="{Binding Source={x:Static resources:Strings.Menu_Bloxstrap_Analytics_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://github.com/bloxstraplabs/bloxstrap/wiki/Privacy-Policy#analytical-functionality'}">
<ui:ToggleSwitch IsChecked="{Binding AnalyticsEnabled, Mode=TwoWay}" />
</controls:OptionControl>
<ui:CardExpander Margin="0,8,0,0" IsExpanded="True">
<ui:CardExpander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock FontSize="14" Text="{x:Static resources:Strings.Menu_Bloxstrap_ExportData_Title}" />
<TextBlock FontSize="12" Text="{x:Static resources:Strings.Menu_Bloxstrap_ExportData_Description}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" TextWrapping="Wrap" />
</StackPanel>
<ui:Button Grid.Column="1" MinWidth="100" Margin="0,0,16,0" Icon="CopySelect20" Content="{x:Static resources:Strings.Common_Export}" Command="{Binding ExportDataCommand}">
<ui:Button.Style>
<Style TargetType="ui:Button" BasedOn="{StaticResource {x:Type ui:Button}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=ShouldExportConfig, Path=IsChecked}" Value="False" />
<Condition Binding="{Binding ElementName=ShouldExportLogs, Path=IsChecked}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger>
</Style.Triggers>
<Setter Property="IsEnabled" Value="True" />
</Style>
</ui:Button.Style>
</ui:Button>
</Grid>
</ui:CardExpander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" Name="ShouldExportConfig" Content="{x:Static resources:Strings.Menu_Bloxstrap_ExportData_ExportConfig}" IsChecked="{Binding ShouldExportConfig}" />
<CheckBox Grid.Column="1" Name="ShouldExportLogs" Content="{x:Static resources:Strings.Menu_Bloxstrap_ExportData_ExportLogs}" IsChecked="{Binding ShouldExportLogs}" />
</Grid>
</ui:CardExpander>
</StackPanel>
</ui:UiPage>

View File

@ -104,7 +104,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
return;
if (dialog.Tabs.SelectedIndex == 0)
AddSingle(dialog.FlagNameTextBox.Text, dialog.FlagValueTextBox.Text);
AddSingle(dialog.FlagNameTextBox.Text.Trim(), dialog.FlagValueTextBox.Text);
else if (dialog.Tabs.SelectedIndex == 1)
ImportJSON(dialog.JsonTextBox.Text);
}

View File

@ -10,6 +10,7 @@
d:DesignHeight="450" d:DesignWidth="800"
Scrollable="True"
Loaded="Page_Loaded"
Unloaded="Page_Unloaded"
Title="FastFlagEditorWarningPage">
<StackPanel VerticalAlignment="Center">

View File

@ -8,25 +8,24 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
/// </summary>
public partial class FastFlagEditorWarningPage
{
private bool _initialLoad = false;
private FastFlagEditorWarningViewModel _viewModel;
public FastFlagEditorWarningPage()
{
DataContext = new FastFlagEditorWarningViewModel(this);
_viewModel = new FastFlagEditorWarningViewModel(this);
DataContext = _viewModel;
InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// refresh datacontext on page load to reset timer
_viewModel.StartCountdown();
}
if (!_initialLoad)
{
_initialLoad = true;
return;
}
DataContext = new FastFlagEditorWarningViewModel(this);
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
_viewModel.StopCountdown();
}
}
}

View File

@ -22,6 +22,12 @@
</StackPanel>
</ui:CardAction>
<controls:OptionControl
Header="{x:Static resources:Strings.Menu_FastFlags_ManagerEnabled_Title}"
Description="{x:Static resources:Strings.Menu_FastFlags_ManagerEnabled_Description}">
<ui:ToggleSwitch IsChecked="{Binding UseFastFlagManager, Mode=TwoWay}" />
</controls:OptionControl>
<TextBlock Text="{x:Static resources:Strings.Common_Presets}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
<TextBlock Text="{x:Static resources:Strings.Menu_FastFlags_Presets_Categories_Rendering}" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
@ -125,7 +131,7 @@
<ui:TextBox Width="200" Padding="10,5,10,5" Text="{Binding FontSize, Mode=TwoWay}" PreviewTextInput="ValidateInt32" />
</controls:OptionControl>
<controls:OptionControl
<!--<controls:OptionControl
Header="{x:Static resources:Strings.Menu_FastFlags_Presets_EscapeMenuVersion_Title}"
HelpLink="https://github.com/bloxstraplabs/bloxstrap/wiki/A-guide-to-FastFlags#escape-menu-version">
<ComboBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" ItemsSource="{Binding IGMenuVersions.Keys, Mode=OneTime}" Text="{Binding SelectedIGMenuVersion, Mode=TwoWay}">
@ -135,7 +141,7 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</controls:OptionControl>
</controls:OptionControl>-->
<controls:OptionControl
Margin="0,24,0,0"

View File

@ -35,7 +35,8 @@
<controls:OptionControl
Header="{x:Static resources:Strings.Menu_Integrations_DesktopApp_Title}"
Description="{x:Static resources:Strings.Menu_Integrations_DesktopApp_Description}"
Description="{Binding Source={x:Static resources:Strings.Menu_Integrations_DesktopApp_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://github.com/bloxstraplabs/bloxstrap/wiki/What-is-activity-tracking%3F#dont-exit-to-desktop-app'}"
HelpLink="https://github.com/bloxstraplabs/bloxstrap/wiki/What-is-activity-tracking%3F#dont-exit-to-desktop-app"
IsEnabled="{Binding InnerContent.IsChecked, ElementName=ActivityTrackingOption, Mode=OneWay}">
<ui:ToggleSwitch IsChecked="{Binding DisableAppPatchEnabled, Mode=TwoWay}" />
</controls:OptionControl>

View File

@ -27,9 +27,13 @@ namespace Bloxstrap.UI
if (crash)
topLine = Strings.Dialog_PlayerError_Crash;
ShowMessageBox($"{topLine}\n\n{Strings.Dialog_PlayerError_HelpInformation}", MessageBoxImage.Error);
string info = String.Format(
Strings.Dialog_PlayerError_HelpInformation,
$"https://github.com/{App.ProjectRepository}/wiki/Roblox-crashes-or-does-not-launch",
$"https://github.com/{App.ProjectRepository}/wiki/Switching-between-Roblox-and-Bloxstrap"
);
Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/wiki/Roblox-crashes-or-does-not-launch");
ShowMessageBox($"{topLine}\n\n{info}", MessageBoxImage.Error);
}
public static void ShowExceptionDialog(Exception exception)

View File

@ -1,4 +1,6 @@
namespace Bloxstrap.UI.ViewModels.About
using System.Windows;
namespace Bloxstrap.UI.ViewModels.About
{
public class SupportersViewModel : NotifyPropertyChangedViewModel
{
@ -8,12 +10,32 @@
public string LoadError { get; set; } = "";
public int Columns { get; set; } = 3;
public SizeChangedEventHandler? WindowResizeEvent;
public SupportersViewModel()
{
WindowResizeEvent += OnWindowResize;
// this will cause momentary freezes only when ran under the debugger
LoadSupporterData();
}
private void OnWindowResize(object sender, SizeChangedEventArgs e)
{
if (!e.WidthChanged)
return;
int newCols = (int)Math.Floor(e.NewSize.Width / 200);
if (Columns == newCols)
return;
Columns = newCols;
OnPropertyChanged(nameof(Columns));
}
public async void LoadSupporterData()
{
const string LOG_IDENT = "AboutViewModel::LoadSupporterData";

View File

@ -1,4 +1,9 @@
namespace Bloxstrap.UI.ViewModels.Settings
using System.Windows.Input;
using CommunityToolkit.Mvvm.Input;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Win32;
namespace Bloxstrap.UI.ViewModels.Settings
{
public class BloxstrapViewModel : NotifyPropertyChangedViewModel
{
@ -13,5 +18,73 @@
get => App.Settings.Prop.EnableAnalytics;
set => App.Settings.Prop.EnableAnalytics = value;
}
public bool ShouldExportConfig { get; set; } = true;
public bool ShouldExportLogs { get; set; } = true;
public ICommand ExportDataCommand => new RelayCommand(ExportData);
private void ExportData()
{
string timestamp = DateTime.UtcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
var dialog = new SaveFileDialog
{
FileName = $"Bloxstrap-export-{timestamp}.zip",
Filter = $"{Strings.FileTypes_ZipArchive}|*.zip"
};
if (dialog.ShowDialog() != true)
return;
using var memStream = new MemoryStream();
using var zipStream = new ZipOutputStream(memStream);
if (ShouldExportConfig)
{
var files = new List<string>()
{
App.Settings.FileLocation,
App.State.FileLocation,
App.FastFlags.FileLocation
};
AddFilesToZipStream(zipStream, files, "Config/");
}
if (ShouldExportLogs && Directory.Exists(Paths.Logs))
{
var files = Directory.GetFiles(Paths.Logs)
.Where(x => !x.Equals(App.Logger.FileLocation, StringComparison.OrdinalIgnoreCase));
AddFilesToZipStream(zipStream, files, "Logs/");
}
zipStream.CloseEntry();
memStream.Position = 0;
using var outputStream = File.OpenWrite(dialog.FileName);
memStream.CopyTo(outputStream);
Process.Start("explorer.exe", $"/select,\"{dialog.FileName}\"");
}
private void AddFilesToZipStream(ZipOutputStream zipStream, IEnumerable<string> files, string directory)
{
foreach (string file in files)
{
if (!File.Exists(file))
continue;
var entry = new ZipEntry(directory + Path.GetFileName(file));
entry.DateTime = DateTime.Now;
zipStream.PutNextEntry(entry);
using var fileStream = File.OpenRead(file);
fileStream.CopyTo(zipStream);
}
}
}
}

View File

@ -13,6 +13,8 @@ namespace Bloxstrap.UI.ViewModels.Settings
{
private Page _page;
private CancellationTokenSource? _cancellationTokenSource;
public string ContinueButtonText { get; set; } = "";
public bool CanContinue { get; set; } = false;
@ -24,17 +26,40 @@ namespace Bloxstrap.UI.ViewModels.Settings
public FastFlagEditorWarningViewModel(Page page)
{
_page = page;
DoCountdown();
}
private async void DoCountdown()
public void StopCountdown()
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = null;
}
public void StartCountdown()
{
StopCountdown();
_cancellationTokenSource = new CancellationTokenSource();
DoCountdown(_cancellationTokenSource.Token);
}
private async void DoCountdown(CancellationToken token)
{
CanContinue = false;
OnPropertyChanged(nameof(CanContinue));
for (int i = 10; i > 0; i--)
{
ContinueButtonText = $"({i}) {Strings.Menu_FastFlagEditor_Warning_Continue}";
OnPropertyChanged(nameof(ContinueButtonText));
await Task.Delay(1000);
try
{
await Task.Delay(1000, token);
}
catch (TaskCanceledException)
{
return;
}
}
ContinueButtonText = Strings.Menu_FastFlagEditor_Warning_Continue;
@ -42,9 +67,6 @@ namespace Bloxstrap.UI.ViewModels.Settings
CanContinue = true;
OnPropertyChanged(nameof(CanContinue));
App.State.Prop.ShowFFlagEditorWarning = false;
App.State.Save();
}
private void Continue()
@ -52,6 +74,9 @@ namespace Bloxstrap.UI.ViewModels.Settings
if (!CanContinue)
return;
App.State.Prop.ShowFFlagEditorWarning = false;
App.State.Save(); // should we be force saving here?
if (Window.GetWindow(_page) is INavigationWindow window)
window.Navigate(typeof(FastFlagEditorPage));
}

View File

@ -1,5 +1,4 @@
using System.Windows;
using System.Windows.Input;
using System.Windows.Input;
using CommunityToolkit.Mvvm.Input;
@ -53,39 +52,39 @@ namespace Bloxstrap.UI.ViewModels.Settings
set => App.FastFlags.SetPreset("Rendering.DisableScaling", value ? "True" : null);
}
public IReadOnlyDictionary<InGameMenuVersion, Dictionary<string, string?>> IGMenuVersions => FastFlagManager.IGMenuVersions;
//public IReadOnlyDictionary<InGameMenuVersion, Dictionary<string, string?>> IGMenuVersions => FastFlagManager.IGMenuVersions;
public InGameMenuVersion SelectedIGMenuVersion
{
get
{
// yeah this kinda sucks
foreach (var version in IGMenuVersions)
{
bool flagsMatch = true;
//public InGameMenuVersion SelectedIGMenuVersion
//{
// get
// {
// // yeah this kinda sucks
// foreach (var version in IGMenuVersions)
// {
// bool flagsMatch = true;
foreach (var flag in version.Value)
{
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;
}
}
// foreach (var flag in version.Value)
// {
// 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)
return version.Key;
}
// if (flagsMatch)
// return version.Key;
// }
return IGMenuVersions.First().Key;
}
// return IGMenuVersions.First().Key;
// }
set
{
foreach (var flag in IGMenuVersions[value])
App.FastFlags.SetPreset($"UI.Menu.Style.{flag.Key}", flag.Value);
}
}
// set
// {
// foreach (var flag in IGMenuVersions[value])
// App.FastFlags.SetPreset($"UI.Menu.Style.{flag.Key}", flag.Value);
// }
//}
public IReadOnlyDictionary<LightingMode, string> LightingModes => FastFlagManager.LightingModes;

View File

@ -42,10 +42,24 @@ namespace Bloxstrap
/// </returns>
public static VersionComparison CompareVersions(string versionStr1, string versionStr2)
{
var version1 = new Version(versionStr1.Replace("v", ""));
var version2 = new Version(versionStr2.Replace("v", ""));
try
{
var version1 = new Version(versionStr1.Replace("v", ""));
var version2 = new Version(versionStr2.Replace("v", ""));
return (VersionComparison)version1.CompareTo(version2);
return (VersionComparison)version1.CompareTo(version2);
}
catch (Exception)
{
// temporary diagnostic log for the issue described here:
// https://github.com/bloxstraplabs/bloxstrap/issues/3193
// the problem is that this happens only on upgrade, so my only hope of catching this is bug reports following the next release
App.Logger.WriteLine("Utilities::CompareVersions", "An exception occurred when comparing versions");
App.Logger.WriteLine("Utilities::CompareVersions", $"versionStr1={versionStr1} versionStr2={versionStr2}");
throw;
}
}
public static string GetRobloxVersion(bool studio)

View File

@ -5,6 +5,8 @@ namespace Bloxstrap.Utility
static class WindowsRegistry
{
private const string RobloxPlaceKey = "Roblox.Place";
public static readonly List<RegistryKey> Roots = new() { Registry.CurrentUser, Registry.LocalMachine };
public static void RegisterProtocol(string key, string name, string handler, string handlerParam = "%1")
{

2
wpfui

@ -1 +1 @@
Subproject commit c4c58c589970a66b27a9de41ab1b6b6539918b52
Subproject commit 9080158ba8d496501146d1167aae910898eff9af