mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-10 15:25:42 -07:00
Merge branch 'main' into feature/improve-studio-support
This commit is contained in:
commit
eb9e1341c7
14
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
14
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -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
|
||||
|
28
.github/workflows/ci-release.yml
vendored
28
.github/workflows/ci-release.yml
vendored
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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\" },
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
string ExecutableName { get; }
|
||||
|
||||
string StartEvent { get; }
|
||||
|
||||
string Directory { get; }
|
||||
|
||||
string OldDirectory { get; }
|
||||
|
||||
string LockFilePath { get; }
|
||||
|
||||
string ExecutablePath { get; }
|
||||
|
@ -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>()
|
||||
|
@ -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>()
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -17,6 +17,8 @@
|
||||
[EnumName(StaticName = "2022")]
|
||||
Icon2022,
|
||||
[EnumName(FromTranslation = "Common.Custom")]
|
||||
IconCustom
|
||||
IconCustom,
|
||||
[EnumName(FromTranslation = "Enums.BootstrapperStyle.ClassicFluentDialog")]
|
||||
IconBloxstrapClassic
|
||||
}
|
||||
}
|
||||
|
10
Bloxstrap/Exceptions/InvalidChannelException.cs
Normal file
10
Bloxstrap/Exceptions/InvalidChannelException.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Bloxstrap.Exceptions
|
||||
{
|
||||
public class InvalidChannelException : Exception
|
||||
{
|
||||
public HttpStatusCode? StatusCode;
|
||||
|
||||
public InvalidChannelException(HttpStatusCode? statusCode) : base()
|
||||
=> StatusCode = statusCode;
|
||||
}
|
||||
}
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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}");
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
12
Bloxstrap/Properties/Resources.Designer.cs
generated
12
Bloxstrap/Properties/Resources.Designer.cs
generated
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
BIN
Bloxstrap/Resources/IconBloxstrapClassic.ico
Normal file
BIN
Bloxstrap/Resources/IconBloxstrapClassic.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
112
Bloxstrap/Resources/Strings.Designer.cs
generated
112
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -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'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't because Roblox's files are still in use.
|
||||
///
|
||||
///Please close any applications that may be using Roblox'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'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't, then this isn'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'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 {
|
||||
|
@ -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>
|
@ -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...");
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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}">
|
||||
|
@ -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;
|
||||
|
@ -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}" />
|
||||
|
@ -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;
|
||||
|
@ -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}"
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Scrollable="True"
|
||||
Loaded="Page_Loaded"
|
||||
Unloaded="Page_Unloaded"
|
||||
Title="FastFlagEditorWarningPage">
|
||||
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
2
wpfui
@ -1 +1 @@
|
||||
Subproject commit c4c58c589970a66b27a9de41ab1b6b6539918b52
|
||||
Subproject commit 9080158ba8d496501146d1167aae910898eff9af
|
Loading…
Reference in New Issue
Block a user