mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge branch 'version-2.2.0'
This commit is contained in:
commit
43555d7a4e
@ -3,7 +3,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Bloxstrap"
|
xmlns:local="clr-namespace:Bloxstrap"
|
||||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
ShutdownMode="OnExplicitShutdown">
|
ShutdownMode="OnExplicitShutdown"
|
||||||
|
DispatcherUnhandledException="GlobalExceptionHandler">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
@ -2,17 +2,21 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
using Bloxstrap.Dialogs;
|
using Bloxstrap.Dialogs;
|
||||||
using Bloxstrap.Enums;
|
using Bloxstrap.Enums;
|
||||||
using Bloxstrap.Helpers;
|
using Bloxstrap.Helpers;
|
||||||
|
using Bloxstrap.Integrations;
|
||||||
using Bloxstrap.Models;
|
using Bloxstrap.Models;
|
||||||
using Bloxstrap.Views;
|
using Bloxstrap.Views;
|
||||||
|
|
||||||
@ -77,35 +81,25 @@ namespace Bloxstrap
|
|||||||
Logger.Initialize(Path.Combine(logdir, $"{ProjectName}_{timestamp}.log"));
|
Logger.Initialize(Path.Combine(logdir, $"{ProjectName}_{timestamp}.log"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
|
||||||
|
Logger.WriteLine("[App::OnStartup] An exception occurred when running the main thread");
|
||||||
|
Logger.WriteLine($"[App::OnStartup] {e.Exception}");
|
||||||
|
|
||||||
|
if (!IsQuiet)
|
||||||
|
Settings.Prop.BootstrapperStyle.GetNew().ShowError($"{e.Exception.GetType()}: {e.Exception.Message}");
|
||||||
|
|
||||||
|
Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
|
|
||||||
Logger.WriteLine($"[App::OnStartup] Starting {ProjectName} v{Version}");
|
Logger.WriteLine($"[App::OnStartup] Starting {ProjectName} v{Version}");
|
||||||
|
|
||||||
// todo: remove this once 32-bit support is fully gone
|
|
||||||
if (!App.IsQuiet && !Environment.Is64BitOperatingSystem)
|
|
||||||
{
|
|
||||||
string message = "In the near future, Roblox will no longer support 32-bit Windows devices. To keep playing Roblox, please use a device that is 64-bit compatible.";
|
|
||||||
|
|
||||||
// check if the processor actually supports 64-bit with wmic
|
|
||||||
// chances are the user just has a 32-bit version of windows installed on 64-bit hardware
|
|
||||||
Process p = new();
|
|
||||||
p.StartInfo.CreateNoWindow = true;
|
|
||||||
p.StartInfo.UseShellExecute = false;
|
|
||||||
p.StartInfo.RedirectStandardOutput = true;
|
|
||||||
p.StartInfo.FileName = "wmic.exe";
|
|
||||||
p.StartInfo.Arguments = "cpu get Architecture";
|
|
||||||
p.Start();
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processor
|
|
||||||
// architecture type 9 is x64
|
|
||||||
if (p.StandardOutput.ReadToEnd().Contains('9'))
|
|
||||||
message += "\n\nYour computer is running a 32-bit version of Windows but is actually 64-bit compatible. Search online for how to upgrade to a 64-bit version of Windows.";
|
|
||||||
|
|
||||||
ShowMessageBox(message, MessageBoxImage.Warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
// To customize application configuration such as set high DPI settings or default font,
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
// see https://aka.ms/applicationconfiguration.
|
// see https://aka.ms/applicationconfiguration.
|
||||||
ApplicationConfiguration.Initialize();
|
ApplicationConfiguration.Initialize();
|
||||||
@ -148,6 +142,29 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: remove this once 32-bit support is fully gone
|
||||||
|
if (!App.IsQuiet && !Environment.Is64BitOperatingSystem)
|
||||||
|
{
|
||||||
|
string message = "In the near future, Roblox will no longer support 32-bit Windows devices. To keep playing Roblox, please use a device that is 64-bit compatible.";
|
||||||
|
|
||||||
|
// check if the processor actually supports 64-bit with wmic
|
||||||
|
// chances are the user just has a 32-bit version of windows installed on 64-bit hardware
|
||||||
|
Process p = new();
|
||||||
|
p.StartInfo.CreateNoWindow = true;
|
||||||
|
p.StartInfo.UseShellExecute = false;
|
||||||
|
p.StartInfo.RedirectStandardOutput = true;
|
||||||
|
p.StartInfo.FileName = "wmic.exe";
|
||||||
|
p.StartInfo.Arguments = "cpu get Architecture";
|
||||||
|
p.Start();
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processor
|
||||||
|
// architecture type 9 is x64
|
||||||
|
if (p.StandardOutput.ReadToEnd().Contains('9'))
|
||||||
|
message += "\n\nYour computer is running a 32-bit version of Windows but is actually 64-bit compatible. Search online for how to upgrade to a 64-bit version of Windows.";
|
||||||
|
|
||||||
|
ShowMessageBox(message, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
// check if installed
|
// check if installed
|
||||||
using (RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"))
|
using (RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"))
|
||||||
{
|
{
|
||||||
@ -158,12 +175,10 @@ namespace Bloxstrap
|
|||||||
BaseDirectory = Path.Combine(Directories.LocalAppData, ProjectName);
|
BaseDirectory = Path.Combine(Directories.LocalAppData, ProjectName);
|
||||||
InitLog();
|
InitLog();
|
||||||
|
|
||||||
// we have reshade enabled by default so we need this
|
|
||||||
FastFlags.SetRenderingMode("Direct3D 11");
|
|
||||||
|
|
||||||
if (!IsQuiet)
|
if (!IsQuiet)
|
||||||
{
|
{
|
||||||
IsSetupComplete = false;
|
IsSetupComplete = false;
|
||||||
|
FastFlags.Load();
|
||||||
new MainWindow().ShowDialog();
|
new MainWindow().ShowDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +197,7 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
|
|
||||||
Directories.Initialize(BaseDirectory);
|
Directories.Initialize(BaseDirectory);
|
||||||
FastFlags.AltFileLocation = Path.Combine(Directories.Modifications, "ClientSettings\\ClientAppSettings.json");
|
FastFlags.Load();
|
||||||
|
|
||||||
// we shouldn't save settings on the first run until the first installation is finished,
|
// we shouldn't save settings on the first run until the first installation is finished,
|
||||||
// just in case the user decides to cancel the install
|
// just in case the user decides to cancel the install
|
||||||
@ -192,9 +207,8 @@ namespace Bloxstrap
|
|||||||
Settings.Load();
|
Settings.Load();
|
||||||
State.Load();
|
State.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!IsUninstall && !IsFirstRun)
|
if (!IsUninstall && !IsFirstRun)
|
||||||
Updater.CheckInstalledVersion();
|
Updater.CheckInstalledVersion();
|
||||||
#endif
|
#endif
|
||||||
@ -221,7 +235,6 @@ namespace Bloxstrap
|
|||||||
ShowMessageBox($"{ProjectName} is currently running, likely as a background Roblox process. Please note that not all your changes will immediately apply until you close all currently open Roblox instances.", MessageBoxImage.Information);
|
ShowMessageBox($"{ProjectName} is currently running, likely as a background Roblox process. Please note that not all your changes will immediately apply until you close all currently open Roblox instances.", MessageBoxImage.Information);
|
||||||
|
|
||||||
new MainWindow().ShowDialog();
|
new MainWindow().ShowDialog();
|
||||||
FastFlags.Save();
|
|
||||||
}
|
}
|
||||||
else if (LaunchArgs.Length > 0)
|
else if (LaunchArgs.Length > 0)
|
||||||
{
|
{
|
||||||
@ -250,6 +263,9 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
DeployManager.Channel = Settings.Prop.Channel;
|
DeployManager.Channel = Settings.Prop.Channel;
|
||||||
|
|
||||||
|
if (Settings.Prop.UseReShade)
|
||||||
|
ReShade.CheckRobloxReleaseChannel().Wait();
|
||||||
|
|
||||||
// start bootstrapper and show the bootstrapper modal if we're not running silently
|
// start bootstrapper and show the bootstrapper modal if we're not running silently
|
||||||
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper");
|
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper");
|
||||||
Bootstrapper bootstrapper = new(commandLine);
|
Bootstrapper bootstrapper = new(commandLine);
|
||||||
@ -308,6 +324,7 @@ namespace Bloxstrap
|
|||||||
#else
|
#else
|
||||||
var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception;
|
var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception;
|
||||||
dialog?.ShowError($"{exception.GetType()}: {exception.Message}");
|
dialog?.ShowError($"{exception.GetType()}: {exception.Message}");
|
||||||
|
Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
|
||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -320,23 +337,10 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
// we've got ownership of the roblox singleton mutex!
|
// we've got ownership of the roblox singleton mutex!
|
||||||
// if we stop running, everything will screw up once any more roblox instances launched
|
// if we stop running, everything will screw up once any more roblox instances launched
|
||||||
while (Utilities.GetProcessCount("RobloxPlayerBeta", false) != 0)
|
while (Process.GetProcessesByName("RobloxPlayerBeta").Any())
|
||||||
{
|
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#if !DEBUG
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.WriteLine("[App::OnStartup] An exception occurred when running the main thread");
|
|
||||||
Logger.WriteLine($"[App::OnStartup] {ex}");
|
|
||||||
|
|
||||||
if (!IsQuiet)
|
|
||||||
Settings.Prop.BootstrapperStyle.GetNew().ShowError($"{ex.GetType()}: {ex.Message}");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Logger.WriteLine($"[App::OnStartup] Successfully reached end of main thread. Terminating...");
|
Logger.WriteLine($"[App::OnStartup] Successfully reached end of main thread. Terminating...");
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<UseWindowsForms>True</UseWindowsForms>
|
<UseWindowsForms>True</UseWindowsForms>
|
||||||
<ApplicationIcon>Bloxstrap.ico</ApplicationIcon>
|
<ApplicationIcon>Bloxstrap.ico</ApplicationIcon>
|
||||||
<Version>2.1.1</Version>
|
<Version>2.2.0</Version>
|
||||||
<FileVersion>2.1.1.0</FileVersion>
|
<FileVersion>2.2.0.0</FileVersion>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -62,24 +62,25 @@ namespace Bloxstrap
|
|||||||
};
|
};
|
||||||
|
|
||||||
private const string AppSettings =
|
private const string AppSettings =
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
|
||||||
"<Settings>\n" +
|
"<Settings>\r\n" +
|
||||||
" <ContentFolder>content</ContentFolder>\n" +
|
" <ContentFolder>content</ContentFolder>\r\n" +
|
||||||
" <BaseUrl>http://www.roblox.com</BaseUrl>\n" +
|
" <BaseUrl>http://www.roblox.com</BaseUrl>\r\n" +
|
||||||
"</Settings>\n";
|
"</Settings>\r\n";
|
||||||
|
|
||||||
private readonly CancellationTokenSource _cancelTokenSource = new();
|
private readonly CancellationTokenSource _cancelTokenSource = new();
|
||||||
|
|
||||||
private static bool FreshInstall => String.IsNullOrEmpty(App.State.Prop.VersionGuid);
|
private static bool FreshInstall => String.IsNullOrEmpty(App.State.Prop.VersionGuid);
|
||||||
private static bool ShouldInstallWebView2 = false;
|
|
||||||
private static string DesktopShortcutLocation => Path.Combine(Directories.Desktop, "Play Roblox.lnk");
|
private static string DesktopShortcutLocation => Path.Combine(Directories.Desktop, "Play Roblox.lnk");
|
||||||
|
private static bool ShouldInstallWebView2 = false;
|
||||||
|
|
||||||
private string? _launchCommandLine;
|
private string _playerLocation => Path.Combine(_versionFolder, "RobloxPlayerBeta.exe");
|
||||||
|
|
||||||
|
private string _launchCommandLine;
|
||||||
|
|
||||||
private string _latestVersionGuid = null!;
|
private string _latestVersionGuid = null!;
|
||||||
private PackageManifest _versionPackageManifest = null!;
|
private PackageManifest _versionPackageManifest = null!;
|
||||||
private string _versionFolder = null!;
|
private string _versionFolder = null!;
|
||||||
private string _playerLocation => Path.Combine(_versionFolder, "RobloxPlayerBeta.exe");
|
|
||||||
|
|
||||||
private bool _isInstalling = false;
|
private bool _isInstalling = false;
|
||||||
private double _progressIncrement;
|
private double _progressIncrement;
|
||||||
@ -91,7 +92,7 @@ namespace Bloxstrap
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Core
|
#region Core
|
||||||
public Bootstrapper(string? launchCommandLine = null)
|
public Bootstrapper(string launchCommandLine)
|
||||||
{
|
{
|
||||||
_launchCommandLine = launchCommandLine;
|
_launchCommandLine = launchCommandLine;
|
||||||
|
|
||||||
@ -116,6 +117,19 @@ namespace Bloxstrap
|
|||||||
Dialog.Message = message;
|
Dialog.Message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateProgressbar()
|
||||||
|
{
|
||||||
|
int newProgress = (int)Math.Floor(_progressIncrement * _totalDownloadedBytes);
|
||||||
|
|
||||||
|
// bugcheck: if we're restoring a file from a package, it'll incorrectly increment the progress beyond 100
|
||||||
|
// too lazy to fix properly so lol
|
||||||
|
if (newProgress > 100)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Dialog is not null)
|
||||||
|
Dialog.ProgressValue = newProgress;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Run()
|
public async Task Run()
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine("[Bootstrapper::Run] Running bootstrapper");
|
App.Logger.WriteLine("[Bootstrapper::Run] Running bootstrapper");
|
||||||
@ -172,10 +186,9 @@ namespace Bloxstrap
|
|||||||
_versionFolder = Path.Combine(Directories.Versions, App.State.Prop.VersionGuid);
|
_versionFolder = Path.Combine(Directories.Versions, App.State.Prop.VersionGuid);
|
||||||
|
|
||||||
if (App.IsFirstRun)
|
if (App.IsFirstRun)
|
||||||
{
|
|
||||||
App.ShouldSaveConfigs = true;
|
App.ShouldSaveConfigs = true;
|
||||||
App.FastFlags.Save();
|
|
||||||
}
|
MigrateIntegrations();
|
||||||
|
|
||||||
if (ShouldInstallWebView2)
|
if (ShouldInstallWebView2)
|
||||||
await InstallWebView2();
|
await InstallWebView2();
|
||||||
@ -185,6 +198,7 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
await ReShade.CheckModifications();
|
await ReShade.CheckModifications();
|
||||||
|
|
||||||
|
App.FastFlags.Save();
|
||||||
await ApplyModifications();
|
await ApplyModifications();
|
||||||
|
|
||||||
if (App.IsFirstRun || FreshInstall)
|
if (App.IsFirstRun || FreshInstall)
|
||||||
@ -192,8 +206,6 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
CheckInstall();
|
CheckInstall();
|
||||||
|
|
||||||
await RbxFpsUnlocker.CheckInstall();
|
|
||||||
|
|
||||||
// at this point we've finished updating our configs
|
// at this point we've finished updating our configs
|
||||||
App.Settings.Save();
|
App.Settings.Save();
|
||||||
App.State.Save();
|
App.State.Save();
|
||||||
@ -207,60 +219,6 @@ namespace Bloxstrap
|
|||||||
await StartRoblox();
|
await StartRoblox();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckForUpdates()
|
|
||||||
{
|
|
||||||
// don't update if there's another instance running (likely running in the background)
|
|
||||||
if (Utilities.GetProcessCount(App.ProjectName) > 1)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] More than one Bloxstrap instance running, aborting update check");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string currentVersion = $"{App.ProjectName} v{App.Version}";
|
|
||||||
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] Checking for {App.ProjectName} updates...");
|
|
||||||
|
|
||||||
var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest");
|
|
||||||
|
|
||||||
if (releaseInfo?.Assets is null || currentVersion == releaseInfo.Name)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] No updates found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetStatus($"Getting the latest {App.ProjectName}...");
|
|
||||||
|
|
||||||
// 64-bit is always the first option
|
|
||||||
GithubReleaseAsset asset = releaseInfo.Assets[Environment.Is64BitOperatingSystem ? 0 : 1];
|
|
||||||
string downloadLocation = Path.Combine(Directories.LocalAppData, "Temp", asset.Name);
|
|
||||||
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] Downloading {releaseInfo.Name}...");
|
|
||||||
|
|
||||||
if (!File.Exists(downloadLocation))
|
|
||||||
{
|
|
||||||
var response = await App.HttpClient.GetAsync(asset.BrowserDownloadUrl);
|
|
||||||
|
|
||||||
await using var fileStream = new FileStream(downloadLocation, FileMode.CreateNew);
|
|
||||||
await response.Content.CopyToAsync(fileStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] Starting {releaseInfo.Name}...");
|
|
||||||
|
|
||||||
ProcessStartInfo startInfo = new()
|
|
||||||
{
|
|
||||||
FileName = downloadLocation,
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (string arg in App.LaunchArgs)
|
|
||||||
startInfo.ArgumentList.Add(arg);
|
|
||||||
|
|
||||||
App.Settings.Save();
|
|
||||||
|
|
||||||
Process.Start(startInfo);
|
|
||||||
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CheckLatestVersion()
|
private async Task CheckLatestVersion()
|
||||||
{
|
{
|
||||||
SetStatus("Connecting to Roblox...");
|
SetStatus("Connecting to Roblox...");
|
||||||
@ -271,73 +229,8 @@ namespace Bloxstrap
|
|||||||
_versionPackageManifest = await PackageManifest.Get(_latestVersionGuid);
|
_versionPackageManifest = await PackageManifest.Get(_latestVersionGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckInstallMigration()
|
|
||||||
{
|
|
||||||
// check if we've changed our install location since the last time we started
|
|
||||||
// in which case, we'll have to copy over all our folders so we don't lose any mods and stuff
|
|
||||||
|
|
||||||
using RegistryKey? applicationKey = Registry.CurrentUser.OpenSubKey($@"Software\{App.ProjectName}", true);
|
|
||||||
|
|
||||||
string? oldInstallLocation = (string?)applicationKey?.GetValue("OldInstallLocation");
|
|
||||||
|
|
||||||
if (applicationKey is null || oldInstallLocation is null || oldInstallLocation == Directories.Base)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SetStatus("Migrating install location...");
|
|
||||||
|
|
||||||
if (Directory.Exists(oldInstallLocation))
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckInstallMigration] Moving all files in {oldInstallLocation} to {Directories.Base}...");
|
|
||||||
|
|
||||||
foreach (string oldFileLocation in Directory.GetFiles(oldInstallLocation, "*.*", SearchOption.AllDirectories))
|
|
||||||
{
|
|
||||||
string relativeFile = oldFileLocation.Substring(oldInstallLocation.Length + 1);
|
|
||||||
string newFileLocation = Path.Combine(Directories.Base, relativeFile);
|
|
||||||
string? newDirectory = Path.GetDirectoryName(newFileLocation);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!String.IsNullOrEmpty(newDirectory))
|
|
||||||
Directory.CreateDirectory(newDirectory);
|
|
||||||
|
|
||||||
File.Move(oldFileLocation, newFileLocation, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckInstallMigration] Failed to move {oldFileLocation} to {newFileLocation}! {ex}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Directory.Delete(oldInstallLocation, true);
|
|
||||||
App.Logger.WriteLine("[Bootstrapper::CheckInstallMigration] Deleted old install location");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::CheckInstallMigration] Failed to delete old install location! {ex}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationKey.DeleteValue("OldInstallLocation");
|
|
||||||
|
|
||||||
// allow shortcuts to be re-registered
|
|
||||||
if (Directory.Exists(Directories.StartMenu))
|
|
||||||
Directory.Delete(Directories.StartMenu, true);
|
|
||||||
|
|
||||||
if (File.Exists(DesktopShortcutLocation))
|
|
||||||
{
|
|
||||||
File.Delete(DesktopShortcutLocation);
|
|
||||||
App.Settings.Prop.CreateDesktopIcon = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
App.Logger.WriteLine("[Bootstrapper::CheckInstallMigration] Finished migrating install location!");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StartRoblox()
|
private async Task StartRoblox()
|
||||||
{
|
{
|
||||||
string startEventName = App.ProjectName.Replace(" ", "") + "StartEvent";
|
|
||||||
|
|
||||||
SetStatus("Starting Roblox...");
|
SetStatus("Starting Roblox...");
|
||||||
|
|
||||||
if (_launchCommandLine == "--app" && App.Settings.Prop.UseDisableAppPatch)
|
if (_launchCommandLine == "--app" && App.Settings.Prop.UseDisableAppPatch)
|
||||||
@ -347,26 +240,29 @@ namespace Bloxstrap
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch time isn't really required for all launches, but it's usually just safest to do this
|
_launchCommandLine = _launchCommandLine.Replace("LAUNCHTIMEPLACEHOLDER", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString());
|
||||||
_launchCommandLine += " --launchtime=" + DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
||||||
|
|
||||||
if (App.Settings.Prop.Channel.ToLower() != DeployManager.DefaultChannel.ToLower())
|
if (App.Settings.Prop.Channel.ToLower() != DeployManager.DefaultChannel.ToLower())
|
||||||
_launchCommandLine += " -channel " + App.Settings.Prop.Channel.ToLower();
|
_launchCommandLine += " -channel " + App.Settings.Prop.Channel.ToLower();
|
||||||
|
|
||||||
_launchCommandLine += " -startEvent " + startEventName;
|
|
||||||
|
|
||||||
// whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes
|
// whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes
|
||||||
bool shouldWait = false;
|
bool shouldWait = false;
|
||||||
|
|
||||||
Process gameClient = Process.Start(_playerLocation, _launchCommandLine);
|
// v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now
|
||||||
|
int gameClientPid;
|
||||||
|
using (Process gameClient = Process.Start(_playerLocation, _launchCommandLine))
|
||||||
|
{
|
||||||
|
gameClientPid = gameClient.Id;
|
||||||
|
}
|
||||||
|
|
||||||
List<Process> autocloseProcesses = new();
|
List<Process> autocloseProcesses = new();
|
||||||
GameActivityWatcher? activityWatcher = null;
|
GameActivityWatcher? activityWatcher = null;
|
||||||
DiscordRichPresence? richPresence = null;
|
DiscordRichPresence? richPresence = null;
|
||||||
ServerNotifier? serverNotifier = null;
|
ServerNotifier? serverNotifier = null;
|
||||||
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Started Roblox (PID {gameClient.Id})");
|
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Started Roblox (PID {gameClientPid})");
|
||||||
|
|
||||||
using (SystemEvent startEvent = new(startEventName))
|
using (SystemEvent startEvent = new("www.roblox.com/robloxStartedEvent"))
|
||||||
{
|
{
|
||||||
bool startEventFired = await startEvent.WaitForEvent();
|
bool startEventFired = await startEvent.WaitForEvent();
|
||||||
|
|
||||||
@ -376,25 +272,6 @@ namespace Bloxstrap
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App.Settings.Prop.RFUEnabled && Process.GetProcessesByName(RbxFpsUnlocker.ApplicationName).Length == 0)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Using rbxfpsunlocker");
|
|
||||||
|
|
||||||
ProcessStartInfo startInfo = new()
|
|
||||||
{
|
|
||||||
WorkingDirectory = Path.Combine(Directories.Integrations, "rbxfpsunlocker"),
|
|
||||||
FileName = Path.Combine(Directories.Integrations, @"rbxfpsunlocker\rbxfpsunlocker.exe")
|
|
||||||
};
|
|
||||||
|
|
||||||
Process process = Process.Start(startInfo)!;
|
|
||||||
|
|
||||||
if (App.Settings.Prop.RFUAutoclose)
|
|
||||||
{
|
|
||||||
shouldWait = true;
|
|
||||||
autocloseProcesses.Add(process);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (App.Settings.Prop.UseDiscordRichPresence || App.Settings.Prop.ShowServerDetails)
|
if (App.Settings.Prop.UseDiscordRichPresence || App.Settings.Prop.ShowServerDetails)
|
||||||
{
|
{
|
||||||
activityWatcher = new();
|
activityWatcher = new();
|
||||||
@ -445,8 +322,11 @@ namespace Bloxstrap
|
|||||||
activityWatcher?.StartWatcher();
|
activityWatcher?.StartWatcher();
|
||||||
|
|
||||||
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Waiting for Roblox to close");
|
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Waiting for Roblox to close");
|
||||||
await gameClient.WaitForExitAsync();
|
|
||||||
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Roblox exited with code {gameClient.ExitCode}");
|
while (Process.GetProcesses().Any(x => x.Id == gameClientPid))
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::StartRoblox] Roblox has exited");
|
||||||
|
|
||||||
richPresence?.Dispose();
|
richPresence?.Dispose();
|
||||||
|
|
||||||
@ -522,6 +402,69 @@ namespace Bloxstrap
|
|||||||
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Registered application");
|
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Registered application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckInstallMigration()
|
||||||
|
{
|
||||||
|
// check if we've changed our install location since the last time we started
|
||||||
|
// in which case, we'll have to copy over all our folders so we don't lose any mods and stuff
|
||||||
|
|
||||||
|
using RegistryKey? applicationKey = Registry.CurrentUser.OpenSubKey($@"Software\{App.ProjectName}", true);
|
||||||
|
|
||||||
|
string? oldInstallLocation = (string?)applicationKey?.GetValue("OldInstallLocation");
|
||||||
|
|
||||||
|
if (applicationKey is null || oldInstallLocation is null || oldInstallLocation == Directories.Base)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetStatus("Migrating install location...");
|
||||||
|
|
||||||
|
if (Directory.Exists(oldInstallLocation))
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckInstallMigration] Moving all files in {oldInstallLocation} to {Directories.Base}...");
|
||||||
|
|
||||||
|
foreach (string oldFileLocation in Directory.GetFiles(oldInstallLocation, "*.*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
string relativeFile = oldFileLocation.Substring(oldInstallLocation.Length + 1);
|
||||||
|
string newFileLocation = Path.Combine(Directories.Base, relativeFile);
|
||||||
|
string? newDirectory = Path.GetDirectoryName(newFileLocation);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(newDirectory))
|
||||||
|
Directory.CreateDirectory(newDirectory);
|
||||||
|
|
||||||
|
File.Move(oldFileLocation, newFileLocation, true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckInstallMigration] Failed to move {oldFileLocation} to {newFileLocation}! {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.Delete(oldInstallLocation, true);
|
||||||
|
App.Logger.WriteLine("[Bootstrapper::CheckInstallMigration] Deleted old install location");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckInstallMigration] Failed to delete old install location! {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applicationKey.DeleteValue("OldInstallLocation");
|
||||||
|
|
||||||
|
// allow shortcuts to be re-registered
|
||||||
|
if (Directory.Exists(Directories.StartMenu))
|
||||||
|
Directory.Delete(Directories.StartMenu, true);
|
||||||
|
|
||||||
|
if (File.Exists(DesktopShortcutLocation))
|
||||||
|
{
|
||||||
|
File.Delete(DesktopShortcutLocation);
|
||||||
|
App.Settings.Prop.CreateDesktopIcon = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
App.Logger.WriteLine("[Bootstrapper::CheckInstallMigration] Finished migrating install location!");
|
||||||
|
}
|
||||||
|
|
||||||
public static void CheckInstall()
|
public static void CheckInstall()
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Checking install");
|
App.Logger.WriteLine("[Bootstrapper::StartRoblox] Checking install");
|
||||||
@ -581,6 +524,60 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CheckForUpdates()
|
||||||
|
{
|
||||||
|
// don't update if there's another instance running (likely running in the background)
|
||||||
|
if (Utilities.GetProcessCount(App.ProjectName) > 1)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] More than one Bloxstrap instance running, aborting update check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string currentVersion = $"{App.ProjectName} v{App.Version}";
|
||||||
|
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] Checking for {App.ProjectName} updates...");
|
||||||
|
|
||||||
|
var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest");
|
||||||
|
|
||||||
|
if (releaseInfo?.Assets is null || currentVersion == releaseInfo.Name)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] No updates found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetStatus($"Getting the latest {App.ProjectName}...");
|
||||||
|
|
||||||
|
// 64-bit is always the first option
|
||||||
|
GithubReleaseAsset asset = releaseInfo.Assets[Environment.Is64BitOperatingSystem ? 0 : 1];
|
||||||
|
string downloadLocation = Path.Combine(Directories.LocalAppData, "Temp", asset.Name);
|
||||||
|
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] Downloading {releaseInfo.Name}...");
|
||||||
|
|
||||||
|
if (!File.Exists(downloadLocation))
|
||||||
|
{
|
||||||
|
var response = await App.HttpClient.GetAsync(asset.BrowserDownloadUrl);
|
||||||
|
|
||||||
|
await using var fileStream = new FileStream(downloadLocation, FileMode.CreateNew);
|
||||||
|
await response.Content.CopyToAsync(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
App.Logger.WriteLine($"[Bootstrapper::CheckForUpdates] Starting {releaseInfo.Name}...");
|
||||||
|
|
||||||
|
ProcessStartInfo startInfo = new()
|
||||||
|
{
|
||||||
|
FileName = downloadLocation,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (string arg in App.LaunchArgs)
|
||||||
|
startInfo.ArgumentList.Add(arg);
|
||||||
|
|
||||||
|
App.Settings.Save();
|
||||||
|
|
||||||
|
Process.Start(startInfo);
|
||||||
|
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
private void Uninstall()
|
private void Uninstall()
|
||||||
{
|
{
|
||||||
// prompt to shutdown roblox if its currently running
|
// prompt to shutdown roblox if its currently running
|
||||||
@ -656,19 +653,6 @@ namespace Bloxstrap
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Roblox Install
|
#region Roblox Install
|
||||||
private void UpdateProgressbar()
|
|
||||||
{
|
|
||||||
int newProgress = (int)Math.Floor(_progressIncrement * _totalDownloadedBytes);
|
|
||||||
|
|
||||||
// bugcheck: if we're restoring a file from a package, it'll incorrectly increment the progress beyond 100
|
|
||||||
// too lazy to fix properly so lol
|
|
||||||
if (newProgress > 100)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Dialog is not null)
|
|
||||||
Dialog.ProgressValue = newProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InstallLatestVersion()
|
private async Task InstallLatestVersion()
|
||||||
{
|
{
|
||||||
_isInstalling = true;
|
_isInstalling = true;
|
||||||
@ -819,6 +803,15 @@ namespace Bloxstrap
|
|||||||
App.Logger.WriteLine($"[Bootstrapper::InstallWebView2] Finished installing runtime");
|
App.Logger.WriteLine($"[Bootstrapper::InstallWebView2] Finished installing runtime");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void MigrateIntegrations()
|
||||||
|
{
|
||||||
|
// v2.2.0 - remove rbxfpsunlocker
|
||||||
|
string rbxfpsunlocker = Path.Combine(Directories.Integrations, "rbxfpsunlocker");
|
||||||
|
|
||||||
|
if (Directory.Exists(rbxfpsunlocker))
|
||||||
|
Directory.Delete(rbxfpsunlocker, true);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ApplyModifications()
|
private async Task ApplyModifications()
|
||||||
{
|
{
|
||||||
SetStatus("Applying Roblox modifications...");
|
SetStatus("Applying Roblox modifications...");
|
||||||
@ -914,7 +907,7 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
packageDirectory = PackageDirectories.First(x => x.Key != "RobloxApp.zip" && fileLocation.StartsWith(x.Value));
|
packageDirectory = PackageDirectories.First(x => x.Value != "" && fileLocation.StartsWith(x.Value));
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
{
|
{
|
||||||
|
@ -76,35 +76,17 @@ namespace Bloxstrap.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// basically any channel that has had a deploy within the past month with a windowsplayer build
|
// most commonly used/interesting channels
|
||||||
public static readonly List<string> ChannelsAbstracted = new()
|
public static readonly List<string> SelectableChannels = new()
|
||||||
{
|
{
|
||||||
"LIVE",
|
"LIVE",
|
||||||
"ZNext",
|
"ZWinPlayer64",
|
||||||
"ZCanary",
|
|
||||||
"ZIntegration"
|
|
||||||
};
|
|
||||||
|
|
||||||
// why not?
|
|
||||||
public static readonly List<string> ChannelsAll = new()
|
|
||||||
{
|
|
||||||
"LIVE",
|
|
||||||
"ZAvatarTeam",
|
|
||||||
"ZAvatarRelease",
|
|
||||||
"ZCanary",
|
|
||||||
"ZCanary1",
|
|
||||||
"ZCanary2",
|
|
||||||
"ZCanary3",
|
|
||||||
"ZCanaryApps",
|
|
||||||
"ZFlag",
|
"ZFlag",
|
||||||
"ZIntegration",
|
|
||||||
"ZIntegration1",
|
|
||||||
"ZLive",
|
|
||||||
"ZLive1",
|
|
||||||
"ZNext",
|
"ZNext",
|
||||||
"ZSocialTeam",
|
"ZCanary",
|
||||||
"ZStudioInt1",
|
"ZIntegration",
|
||||||
"ZStudioInt2"
|
"ZAvatarTeam",
|
||||||
|
"ZSocialTeam"
|
||||||
};
|
};
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -112,7 +94,7 @@ namespace Bloxstrap.Helpers
|
|||||||
{
|
{
|
||||||
App.Logger.WriteLine($"[DeployManager::GetLastDeploy] Getting deploy info for channel {Channel} (timestamp={timestamp})");
|
App.Logger.WriteLine($"[DeployManager::GetLastDeploy] Getting deploy info for channel {Channel} (timestamp={timestamp})");
|
||||||
|
|
||||||
HttpResponseMessage deployInfoResponse = await App.HttpClient.GetAsync($"https://clientsettings.roblox.com/v2/client-version/WindowsPlayer/channel/{Channel}");
|
HttpResponseMessage deployInfoResponse = await App.HttpClient.GetAsync($"https://clientsettings.roblox.com/v2/client-version/WindowsPlayer/channel/{Channel}").ConfigureAwait(false);
|
||||||
|
|
||||||
string rawResponse = await deployInfoResponse.Content.ReadAsStringAsync();
|
string rawResponse = await deployInfoResponse.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -15,7 +16,7 @@ namespace Bloxstrap.Helpers
|
|||||||
public Dictionary<string, object?> Changes = new();
|
public Dictionary<string, object?> Changes = new();
|
||||||
|
|
||||||
// only one missing here is Metal because lol
|
// only one missing here is Metal because lol
|
||||||
public static IReadOnlyDictionary<string, string> RenderingModes { get; set; } = new Dictionary<string, string>()
|
public static IReadOnlyDictionary<string, string> RenderingModes => new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ "Automatic", "" },
|
{ "Automatic", "" },
|
||||||
{ "Direct3D 11", "FFlagDebugGraphicsPreferD3D11" },
|
{ "Direct3D 11", "FFlagDebugGraphicsPreferD3D11" },
|
||||||
@ -23,10 +24,63 @@ namespace Bloxstrap.Helpers
|
|||||||
{ "Vulkan", "FFlagDebugGraphicsPreferVulkan" }
|
{ "Vulkan", "FFlagDebugGraphicsPreferVulkan" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// this is one hell of a variable definition lmao
|
||||||
|
public static IReadOnlyDictionary<string, Dictionary<string, string?>> IGMenuVersions => new Dictionary<string, Dictionary<string, string?>>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Default",
|
||||||
|
new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "FFlagDisableNewIGMinDUA", null },
|
||||||
|
{ "FFlagEnableInGameMenuV3", null }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"Version 1 (2015)",
|
||||||
|
new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "FFlagDisableNewIGMinDUA", "True" },
|
||||||
|
{ "FFlagEnableInGameMenuV3", "False" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"Version 2 (2020)",
|
||||||
|
new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "FFlagDisableNewIGMinDUA", "False" },
|
||||||
|
{ "FFlagEnableInGameMenuV3", "False" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"Version 3 (2021)",
|
||||||
|
new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
{ "FFlagDisableNewIGMinDUA", "False" },
|
||||||
|
{ "FFlagEnableInGameMenuV3", "True" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// all fflags are stored as strings
|
||||||
|
// to delete a flag, set the value as null
|
||||||
|
public void SetValue(string key, object? value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
Changes[key] = null;
|
||||||
|
App.Logger.WriteLine($"[FastFlagManager::SetValue] Deletion of '{key}' is pending");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Changes[key] = value.ToString();
|
||||||
|
App.Logger.WriteLine($"[FastFlagManager::SetValue] Value change for '{key}' to '{value}' is pending");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this returns null if the fflag doesn't exist
|
// this returns null if the fflag doesn't exist
|
||||||
// this also returns as a string because deserializing an object doesn't
|
|
||||||
// deserialize back into the original object type, it instead deserializes
|
|
||||||
// as a "JsonElement" which is annoying
|
|
||||||
public string? GetValue(string key)
|
public string? GetValue(string key)
|
||||||
{
|
{
|
||||||
// check if we have an updated change for it pushed first
|
// check if we have an updated change for it pushed first
|
||||||
@ -44,26 +98,39 @@ namespace Bloxstrap.Helpers
|
|||||||
foreach (var mode in RenderingModes)
|
foreach (var mode in RenderingModes)
|
||||||
{
|
{
|
||||||
if (mode.Key != "Automatic")
|
if (mode.Key != "Automatic")
|
||||||
App.FastFlags.Changes[mode.Value] = null;
|
SetValue(mode.Value, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value != "Automatic")
|
if (value != "Automatic")
|
||||||
App.FastFlags.Changes[RenderingModes[value]] = true;
|
SetValue(RenderingModes[value], "True");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load()
|
||||||
|
{
|
||||||
|
base.Load();
|
||||||
|
|
||||||
|
// set to 9999 by default if it doesnt already exist
|
||||||
|
if (GetValue("DFIntTaskSchedulerTargetFps") is null)
|
||||||
|
SetValue("DFIntTaskSchedulerTargetFps", 9999);
|
||||||
|
|
||||||
|
// reshade / exclusive fullscreen requires direct3d 11 to work
|
||||||
|
if (GetValue(RenderingModes["Direct3D 11"]) != "True" && (App.Settings.Prop.UseReShade || App.FastFlags.GetValue("FFlagHandleAltEnterFullscreenManually") == "False"))
|
||||||
|
SetRenderingMode("Direct3D 11");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Save()
|
public override void Save()
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine($"[FastFlagManager::Save] Attempting to save JSON to {FileLocation}...");
|
App.Logger.WriteLine($"[FastFlagManager::Save] Attempting to save JSON to {FileLocation}...");
|
||||||
|
|
||||||
|
// reload for any changes made while the menu was open
|
||||||
|
Load();
|
||||||
|
|
||||||
if (Changes.Count == 0)
|
if (Changes.Count == 0)
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine($"[FastFlagManager::Save] No changes to apply, aborting.");
|
App.Logger.WriteLine($"[FastFlagManager::Save] No changes to apply, aborting.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload for any changes made while the menu was open
|
|
||||||
Load();
|
|
||||||
|
|
||||||
foreach (var change in Changes)
|
foreach (var change in Changes)
|
||||||
{
|
{
|
||||||
if (change.Value is null)
|
if (change.Value is null)
|
||||||
@ -73,7 +140,7 @@ namespace Bloxstrap.Helpers
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
App.Logger.WriteLine($"[FastFlagManager::Save] Setting '{change.Key}' to {change.Value}");
|
App.Logger.WriteLine($"[FastFlagManager::Save] Setting '{change.Key}' to '{change.Value}'");
|
||||||
Prop[change.Key] = change.Value;
|
Prop[change.Key] = change.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,32 +1,27 @@
|
|||||||
using Bloxstrap.Models;
|
using System;
|
||||||
using Bloxstrap.Properties;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Bloxstrap.Helpers
|
namespace Bloxstrap.Helpers
|
||||||
{
|
{
|
||||||
public class JsonManager<T> where T : new()
|
public class JsonManager<T> where T : new()
|
||||||
{
|
{
|
||||||
public T Prop { get; set; } = new();
|
public T Prop { get; set; } = new();
|
||||||
public virtual string FileLocation => AltFileLocation ?? Path.Combine(Directories.Base, $"{typeof(T).Name}.json");
|
public virtual string FileLocation => Path.Combine(Directories.Base, $"{typeof(T).Name}.json");
|
||||||
public string? AltFileLocation { get; set; }
|
|
||||||
|
|
||||||
public void Load()
|
public virtual void Load()
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine($"[JsonManager<{typeof(T).Name}>::Load] Loading JSON from {FileLocation}...");
|
App.Logger.WriteLine($"[JsonManager<{typeof(T).Name}>::Load] Loading JSON from {FileLocation}...");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
T? settings = JsonSerializer.Deserialize<T>(File.ReadAllText(FileLocation));
|
T? settings = JsonSerializer.Deserialize<T>(File.ReadAllText(FileLocation));
|
||||||
Prop = settings ?? throw new ArgumentNullException("Deserialization returned null");
|
|
||||||
|
if (settings is null)
|
||||||
|
throw new ArgumentNullException("Deserialization returned null");
|
||||||
|
|
||||||
|
Prop = settings;
|
||||||
|
|
||||||
App.Logger.WriteLine($"[JsonManager<{typeof(T).Name}>::Load] JSON loaded successfully!");
|
App.Logger.WriteLine($"[JsonManager<{typeof(T).Name}>::Load] JSON loaded successfully!");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -18,7 +18,7 @@ namespace Bloxstrap.Helpers
|
|||||||
{ "launchmode", "--" },
|
{ "launchmode", "--" },
|
||||||
{ "gameinfo", "-t " },
|
{ "gameinfo", "-t " },
|
||||||
{ "placelauncherurl", "-j "},
|
{ "placelauncherurl", "-j "},
|
||||||
// { "launchtime", "--launchtime=" }, we'll set this when launching the game client
|
{ "launchtime", "--launchtime=" },
|
||||||
{ "browsertrackerid", "-b " },
|
{ "browsertrackerid", "-b " },
|
||||||
{ "robloxLocale", "--rloc " },
|
{ "robloxLocale", "--rloc " },
|
||||||
{ "gameLocale", "--gloc " },
|
{ "gameLocale", "--gloc " },
|
||||||
@ -50,6 +50,10 @@ namespace Bloxstrap.Helpers
|
|||||||
if (key == "placelauncherurl")
|
if (key == "placelauncherurl")
|
||||||
val = HttpUtility.UrlDecode(val);
|
val = HttpUtility.UrlDecode(val);
|
||||||
|
|
||||||
|
// we'll set this before launching because for some reason roblox just refuses to launch if its like a few minutes old so ???
|
||||||
|
if (key == "launchtime")
|
||||||
|
val = "LAUNCHTIMEPLACEHOLDER";
|
||||||
|
|
||||||
if (key == "channel")
|
if (key == "channel")
|
||||||
{
|
{
|
||||||
if (val.ToLower() != App.Settings.Prop.Channel.ToLower())
|
if (val.ToLower() != App.Settings.Prop.Channel.ToLower())
|
||||||
|
@ -78,14 +78,6 @@ namespace Bloxstrap.Helpers
|
|||||||
|
|
||||||
Bootstrapper.Register();
|
Bootstrapper.Register();
|
||||||
|
|
||||||
// update check: if we're upgrading to v2.1.0 and have reshade enabled,
|
|
||||||
// we need to set our renderer to direct3d 11
|
|
||||||
if (App.Settings.Prop.UseReShade && App.FastFlags.GetValue(FastFlagManager.RenderingModes["Direct3D 11"]) is null)
|
|
||||||
{
|
|
||||||
App.FastFlags.SetRenderingMode("Direct3D 11");
|
|
||||||
App.FastFlags.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAutoUpgrade)
|
if (isAutoUpgrade)
|
||||||
{
|
{
|
||||||
NotifyIcon notification = new()
|
NotifyIcon notification = new()
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bloxstrap.Helpers;
|
|
||||||
|
|
||||||
using Bloxstrap.Models;
|
|
||||||
|
|
||||||
namespace Bloxstrap.Integrations
|
|
||||||
{
|
|
||||||
internal class RbxFpsUnlocker
|
|
||||||
{
|
|
||||||
public const string ApplicationName = "rbxfpsunlocker";
|
|
||||||
public const string ProjectRepository = "axstin/rbxfpsunlocker";
|
|
||||||
|
|
||||||
// default settings but with QuickStart set to true and CheckForUpdates set to false
|
|
||||||
private static readonly string Settings =
|
|
||||||
"UnlockClient=true\n" +
|
|
||||||
"UnlockStudio=false\n" +
|
|
||||||
"FPSCapValues=[30.000000, 60.000000, 75.000000, 120.000000, 144.000000, 165.000000, 240.000000, 360.000000]\n" +
|
|
||||||
"FPSCapSelection=0\n" +
|
|
||||||
"FPSCap=0.000000\n" +
|
|
||||||
"CheckForUpdates=false\n" +
|
|
||||||
"NonBlockingErrors=true\n" +
|
|
||||||
"SilentErrors=false\n" +
|
|
||||||
"QuickStart=true\n";
|
|
||||||
|
|
||||||
public static void CheckIfRunning()
|
|
||||||
{
|
|
||||||
Process[] processes = Process.GetProcessesByName(ApplicationName);
|
|
||||||
|
|
||||||
if (processes.Length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
App.Logger.WriteLine("[RbxFpsUnlocker::CheckIfRunning] Closing currently running rbxfpsunlocker processes...");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (Process process in processes)
|
|
||||||
{
|
|
||||||
if (process.MainModule?.FileName is null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!process.MainModule.FileName.Contains(Directories.Base))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
process.Kill();
|
|
||||||
process.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
App.Logger.WriteLine($"[RbxFpsUnlocker::CheckIfRunning] Could not close rbxfpsunlocker process! {ex}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task CheckInstall()
|
|
||||||
{
|
|
||||||
string folderLocation = Path.Combine(Directories.Base, "Integrations\\rbxfpsunlocker");
|
|
||||||
string fileLocation = Path.Combine(folderLocation, "rbxfpsunlocker.exe");
|
|
||||||
string settingsLocation = Path.Combine(folderLocation, "settings");
|
|
||||||
|
|
||||||
if (!App.Settings.Prop.RFUEnabled)
|
|
||||||
{
|
|
||||||
// don't delete rbxfpsunlocker if rbxfpsunlocker and roblox is currently running
|
|
||||||
if (Utilities.CheckIfProcessRunning(ApplicationName) && Utilities.CheckIfRobloxRunning())
|
|
||||||
return;
|
|
||||||
|
|
||||||
App.State.Prop.RbxFpsUnlockerVersion = "";
|
|
||||||
App.State.Save();
|
|
||||||
|
|
||||||
if (Directory.Exists(folderLocation))
|
|
||||||
{
|
|
||||||
CheckIfRunning();
|
|
||||||
Directory.Delete(folderLocation, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
|
|
||||||
|
|
||||||
if (releaseInfo is null || releaseInfo.Assets is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string downloadUrl = releaseInfo.Assets[0].BrowserDownloadUrl;
|
|
||||||
|
|
||||||
DirectoryInfo directory = new(folderLocation);
|
|
||||||
directory.Create();
|
|
||||||
// i have no idea how the read only flag enables itself but apparently it just does
|
|
||||||
directory.Attributes &= ~FileAttributes.ReadOnly;
|
|
||||||
|
|
||||||
if (File.Exists(fileLocation))
|
|
||||||
{
|
|
||||||
// no new release published, return
|
|
||||||
if (App.State.Prop.RbxFpsUnlockerVersion == releaseInfo.TagName)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CheckIfRunning();
|
|
||||||
File.Delete(fileLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
App.Logger.WriteLine("[RbxFpsUnlocker::CheckInstall] Installing/Updating rbxfpsunlocker...");
|
|
||||||
|
|
||||||
{
|
|
||||||
byte[] bytes = await App.HttpClient.GetByteArrayAsync(downloadUrl);
|
|
||||||
|
|
||||||
using MemoryStream zipStream = new(bytes);
|
|
||||||
using ZipArchive archive = new(zipStream);
|
|
||||||
|
|
||||||
archive.ExtractToDirectory(folderLocation, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(settingsLocation))
|
|
||||||
await File.WriteAllTextAsync(settingsLocation, Settings);
|
|
||||||
|
|
||||||
App.State.Prop.RbxFpsUnlockerVersion = releaseInfo.TagName;
|
|
||||||
App.State.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bloxstrap.Helpers;
|
using System.Windows;
|
||||||
|
|
||||||
|
using Bloxstrap.Helpers;
|
||||||
using Bloxstrap.Models;
|
using Bloxstrap.Models;
|
||||||
|
|
||||||
using IniParser;
|
using IniParser;
|
||||||
@ -501,5 +500,35 @@ namespace Bloxstrap.Integrations
|
|||||||
|
|
||||||
SynchronizeConfigFile();
|
SynchronizeConfigFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task CheckRobloxReleaseChannel()
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[ReShade::CheckRobloxReleaseChannel] Checking current Roblox release channel ({App.Settings.Prop.Channel})...");
|
||||||
|
|
||||||
|
if (App.Settings.Prop.Channel.ToLower() == DeployManager.DefaultChannel.ToLower())
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine($"[App::OnStartup] Channel is already {DeployManager.DefaultChannel}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientVersion versionInfo = await App.DeployManager.GetLastDeploy().ConfigureAwait(false);
|
||||||
|
string manifest = await App.HttpClient.GetStringAsync($"{App.DeployManager.BaseUrl}/{versionInfo.VersionGuid}-rbxManifest.txt");
|
||||||
|
|
||||||
|
if (!manifest.Contains("RobloxPlayerBeta.dll"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MessageBoxResult result = !App.Settings.Prop.PromptChannelChange ? MessageBoxResult.Yes : App.ShowMessageBox(
|
||||||
|
$"You currently have ReShade enabled, however your current preferred channel ({App.Settings.Prop.Channel}) does not support ReShade. Would you like to switch to {DeployManager.DefaultChannel}? ",
|
||||||
|
MessageBoxImage.Question,
|
||||||
|
MessageBoxButton.YesNo
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
App.Logger.WriteLine($"[App::OnStartup] Changed Roblox build channel from {App.Settings.Prop.Channel} to {DeployManager.DefaultChannel}");
|
||||||
|
App.DeployManager.Channel = App.Settings.Prop.Channel = DeployManager.DefaultChannel;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@ namespace Bloxstrap.Models
|
|||||||
// integration configuration
|
// integration configuration
|
||||||
public bool UseDiscordRichPresence { get; set; } = true;
|
public bool UseDiscordRichPresence { get; set; } = true;
|
||||||
public bool HideRPCButtons { get; set; } = true;
|
public bool HideRPCButtons { get; set; } = true;
|
||||||
public bool RFUEnabled { get; set; } = false;
|
|
||||||
public bool RFUAutoclose { get; set; } = false;
|
|
||||||
public bool UseReShade { get; set; } = true;
|
public bool UseReShade { get; set; } = true;
|
||||||
public bool UseReShadeExtraviPresets { get; set; } = true;
|
public bool UseReShadeExtraviPresets { get; set; } = true;
|
||||||
public bool ShowServerDetails { get; set; } = false;
|
public bool ShowServerDetails { get; set; } = false;
|
||||||
|
@ -9,7 +9,6 @@ namespace Bloxstrap.Models
|
|||||||
public class State
|
public class State
|
||||||
{
|
{
|
||||||
public string VersionGuid { get; set; } = "";
|
public string VersionGuid { get; set; } = "";
|
||||||
public string RbxFpsUnlockerVersion { get; set; } = "";
|
|
||||||
public string ReShadeConfigVersion { get; set; } = "";
|
public string ReShadeConfigVersion { get; set; } = "";
|
||||||
public string ExtraviReShadePresetsVersion { get; set; } = "";
|
public string ExtraviReShadePresetsVersion { get; set; } = "";
|
||||||
public List<string> ModManifest { get; set; } = new();
|
public List<string> ModManifest { get; set; } = new();
|
||||||
|
@ -118,7 +118,10 @@ namespace Bloxstrap.ViewModels
|
|||||||
get => App.Settings.Prop.BootstrapperIconCustomLocation;
|
get => App.Settings.Prop.BootstrapperIconCustomLocation;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
App.Settings.Prop.BootstrapperIcon = BootstrapperIcon.IconCustom;
|
||||||
App.Settings.Prop.BootstrapperIconCustomLocation = value;
|
App.Settings.Prop.BootstrapperIconCustomLocation = value;
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(Icon));
|
||||||
OnPropertyChanged(nameof(IconPreviewSource));
|
OnPropertyChanged(nameof(IconPreviewSource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
using Bloxstrap.Enums;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Forms;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using System.Windows.Forms;
|
|
||||||
using Wpf.Ui.Mvvm.Interfaces;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using Bloxstrap.Helpers;
|
using Bloxstrap.Helpers;
|
||||||
using Bloxstrap.Models;
|
using Bloxstrap.Models;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Bloxstrap.ViewModels
|
namespace Bloxstrap.ViewModels
|
||||||
{
|
{
|
||||||
@ -20,14 +18,13 @@ namespace Bloxstrap.ViewModels
|
|||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
|
||||||
private IEnumerable<string> _channels = DeployManager.ChannelsAbstracted.Contains(App.Settings.Prop.Channel) ? DeployManager.ChannelsAbstracted : DeployManager.ChannelsAll;
|
private bool _manualChannelEntry = !DeployManager.SelectableChannels.Contains(App.Settings.Prop.Channel);
|
||||||
private bool _showAllChannels = !DeployManager.ChannelsAbstracted.Contains(App.Settings.Prop.Channel);
|
|
||||||
|
|
||||||
public ICommand BrowseInstallLocationCommand => new RelayCommand(BrowseInstallLocation);
|
public ICommand BrowseInstallLocationCommand => new RelayCommand(BrowseInstallLocation);
|
||||||
public ICommand OpenFolderCommand => new RelayCommand(OpenFolder);
|
public ICommand OpenFolderCommand => new RelayCommand(OpenFolder);
|
||||||
|
|
||||||
|
public DeployInfo? ChannelDeployInfo { get; private set; } = null;
|
||||||
public DeployInfo? ChannelDeployInfo { get; private set; } = null; //new DeployInfo(){ Version = "hi", VersionGuid = "hi", Timestamp = "January 25 2023 at 6:03:48 PM" };
|
public string ChannelInfoLoadingText { get; private set; } = null!;
|
||||||
|
|
||||||
public InstallationViewModel()
|
public InstallationViewModel()
|
||||||
{
|
{
|
||||||
@ -36,10 +33,16 @@ namespace Bloxstrap.ViewModels
|
|||||||
|
|
||||||
private async Task LoadChannelDeployInfo(string channel)
|
private async Task LoadChannelDeployInfo(string channel)
|
||||||
{
|
{
|
||||||
|
ChannelInfoLoadingText = "Fetching latest deploy info, please wait...";
|
||||||
|
OnPropertyChanged(nameof(ChannelInfoLoadingText));
|
||||||
|
|
||||||
ChannelDeployInfo = null;
|
ChannelDeployInfo = null;
|
||||||
OnPropertyChanged(nameof(ChannelDeployInfo));
|
OnPropertyChanged(nameof(ChannelDeployInfo));
|
||||||
|
|
||||||
App.DeployManager.Channel = channel;
|
App.DeployManager.Channel = channel;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
ClientVersion info = await App.DeployManager.GetLastDeploy(true);
|
ClientVersion info = await App.DeployManager.GetLastDeploy(true);
|
||||||
|
|
||||||
ChannelDeployInfo = new DeployInfo
|
ChannelDeployInfo = new DeployInfo
|
||||||
@ -51,6 +54,12 @@ namespace Bloxstrap.ViewModels
|
|||||||
|
|
||||||
OnPropertyChanged(nameof(ChannelDeployInfo));
|
OnPropertyChanged(nameof(ChannelDeployInfo));
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
ChannelInfoLoadingText = "Failed to get deploy info.\nIs the channel name valid?";
|
||||||
|
OnPropertyChanged(nameof(ChannelInfoLoadingText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void BrowseInstallLocation()
|
private void BrowseInstallLocation()
|
||||||
{
|
{
|
||||||
@ -74,43 +83,36 @@ namespace Bloxstrap.ViewModels
|
|||||||
set => App.BaseDirectory = value;
|
set => App.BaseDirectory = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> Channels
|
public IEnumerable<string> Channels => DeployManager.SelectableChannels;
|
||||||
{
|
|
||||||
get => _channels;
|
|
||||||
set => _channels = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Channel
|
public string Channel
|
||||||
{
|
{
|
||||||
get => App.Settings.Prop.Channel;
|
get => App.Settings.Prop.Channel;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
//Task.Run(() => GetChannelInfo(value));
|
|
||||||
Task.Run(() => LoadChannelDeployInfo(value));
|
Task.Run(() => LoadChannelDeployInfo(value));
|
||||||
App.Settings.Prop.Channel = value;
|
App.Settings.Prop.Channel = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowAllChannels
|
public bool ManualChannelEntry
|
||||||
{
|
{
|
||||||
get => _showAllChannels;
|
get => _manualChannelEntry;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value)
|
_manualChannelEntry = value;
|
||||||
{
|
|
||||||
Channels = DeployManager.ChannelsAll;
|
if (!value && !Channels.Contains(Channel))
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Channels = DeployManager.ChannelsAbstracted;
|
|
||||||
Channel = DeployManager.DefaultChannel;
|
Channel = DeployManager.DefaultChannel;
|
||||||
|
|
||||||
OnPropertyChanged(nameof(Channel));
|
OnPropertyChanged(nameof(Channel));
|
||||||
|
OnPropertyChanged(nameof(ChannelComboBoxVisibility));
|
||||||
|
OnPropertyChanged(nameof(ChannelTextBoxVisibility));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPropertyChanged(nameof(Channels));
|
// cant use data bindings so i have to do whatever tf this is
|
||||||
|
public Visibility ChannelComboBoxVisibility => ManualChannelEntry ? Visibility.Collapsed : Visibility.Visible;
|
||||||
_showAllChannels = value;
|
public Visibility ChannelTextBoxVisibility => ManualChannelEntry ? Visibility.Visible : Visibility.Collapsed;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,23 +97,6 @@ namespace Bloxstrap.ViewModels
|
|||||||
set => App.Settings.Prop.UseReShadeExtraviPresets = value;
|
set => App.Settings.Prop.UseReShadeExtraviPresets = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RbxFpsUnlockerEnabled
|
|
||||||
{
|
|
||||||
get => App.Settings.Prop.RFUEnabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
App.Settings.Prop.RFUEnabled = value;
|
|
||||||
RbxFpsUnlockerAutocloseEnabled = value;
|
|
||||||
OnPropertyChanged(nameof(RbxFpsUnlockerAutocloseEnabled));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RbxFpsUnlockerAutocloseEnabled
|
|
||||||
{
|
|
||||||
get => App.Settings.Prop.RFUAutoclose;
|
|
||||||
set => App.Settings.Prop.RFUAutoclose = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowServerDetailsEnabled
|
public bool ShowServerDetailsEnabled
|
||||||
{
|
{
|
||||||
get => App.Settings.Prop.ShowServerDetails;
|
get => App.Settings.Prop.ShowServerDetails;
|
||||||
|
@ -60,6 +60,7 @@ namespace Bloxstrap.ViewModels
|
|||||||
if (!App.IsFirstRun)
|
if (!App.IsFirstRun)
|
||||||
{
|
{
|
||||||
App.ShouldSaveConfigs = true;
|
App.ShouldSaveConfigs = true;
|
||||||
|
App.FastFlags.Save();
|
||||||
|
|
||||||
if (App.BaseDirectory != _originalBaseDirectory)
|
if (App.BaseDirectory != _originalBaseDirectory)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@ -14,10 +15,7 @@ namespace Bloxstrap.ViewModels
|
|||||||
|
|
||||||
public ICommand OpenModsFolderCommand => new RelayCommand(OpenModsFolder);
|
public ICommand OpenModsFolderCommand => new RelayCommand(OpenModsFolder);
|
||||||
|
|
||||||
private void OpenModsFolder()
|
private void OpenModsFolder() => Process.Start("explorer.exe", Directories.Modifications);
|
||||||
{
|
|
||||||
Process.Start("explorer.exe", Directories.Modifications);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OldDeathSoundEnabled
|
public bool OldDeathSoundEnabled
|
||||||
{
|
{
|
||||||
@ -37,24 +35,14 @@ namespace Bloxstrap.ViewModels
|
|||||||
set => App.Settings.Prop.UseDisableAppPatch = value;
|
set => App.Settings.Prop.UseDisableAppPatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int FramerateLimit
|
||||||
|
{
|
||||||
|
get => Int32.TryParse(App.FastFlags.GetValue("DFIntTaskSchedulerTargetFps"), out int x) ? x : 60;
|
||||||
|
set => App.FastFlags.SetValue("DFIntTaskSchedulerTargetFps", value);
|
||||||
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, string> RenderingModes => FastFlagManager.RenderingModes;
|
public IReadOnlyDictionary<string, string> RenderingModes => FastFlagManager.RenderingModes;
|
||||||
|
|
||||||
// this flag has to be set to false to work, weirdly enough
|
|
||||||
public bool ExclusiveFullscreenEnabled
|
|
||||||
{
|
|
||||||
get => App.FastFlags.GetValue("FFlagHandleAltEnterFullscreenManually") == "False";
|
|
||||||
set
|
|
||||||
{
|
|
||||||
App.FastFlags.Changes["FFlagHandleAltEnterFullscreenManually"] = value ? false : null;
|
|
||||||
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
App.FastFlags.SetRenderingMode("Direct3D 11");
|
|
||||||
OnPropertyChanged(nameof(SelectedRenderingMode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SelectedRenderingMode
|
public string SelectedRenderingMode
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -71,6 +59,61 @@ namespace Bloxstrap.ViewModels
|
|||||||
set => App.FastFlags.SetRenderingMode(value);
|
set => App.FastFlags.SetRenderingMode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this flag has to be set to false to work, weirdly enough
|
||||||
|
public bool ExclusiveFullscreenEnabled
|
||||||
|
{
|
||||||
|
get => App.FastFlags.GetValue("FFlagHandleAltEnterFullscreenManually") == "False";
|
||||||
|
set
|
||||||
|
{
|
||||||
|
App.FastFlags.SetValue("FFlagHandleAltEnterFullscreenManually", value ? "False" : null);
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
App.FastFlags.SetRenderingMode("Direct3D 11");
|
||||||
|
OnPropertyChanged(nameof(SelectedRenderingMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Dictionary<string, string?>> IGMenuVersions => FastFlagManager.IGMenuVersions;
|
||||||
|
|
||||||
|
public string SelectedIGMenuVersion
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// yeah this kinda sucks
|
||||||
|
foreach (var version in IGMenuVersions)
|
||||||
|
{
|
||||||
|
bool flagsMatch = true;
|
||||||
|
|
||||||
|
foreach (var flag in version.Value)
|
||||||
|
{
|
||||||
|
if (App.FastFlags.GetValue(flag.Key) != flag.Value)
|
||||||
|
flagsMatch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flagsMatch)
|
||||||
|
return version.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Default";
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
foreach (var flag in IGMenuVersions[value])
|
||||||
|
{
|
||||||
|
App.FastFlags.SetValue(flag.Key, flag.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AlternateGraphicsSelectorEnabled
|
||||||
|
{
|
||||||
|
get => App.FastFlags.GetValue("FFlagFixGraphicsQuality") == "True";
|
||||||
|
set => App.FastFlags.SetValue("FFlagFixGraphicsQuality", value ? "True" : null);
|
||||||
|
}
|
||||||
|
|
||||||
public bool DisableFullscreenOptimizationsEnabled
|
public bool DisableFullscreenOptimizationsEnabled
|
||||||
{
|
{
|
||||||
get => App.Settings.Prop.DisableFullscreenOptimizations;
|
get => App.Settings.Prop.DisableFullscreenOptimizations;
|
||||||
|
@ -137,12 +137,6 @@
|
|||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="BSD 3-Clause License" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="BSD 3-Clause License" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ui:CardAction>
|
</ui:CardAction>
|
||||||
<ui:CardAction Grid.Row="4" Grid.Column="2" Margin="0,8,0,0" Padding="16,13,16,12" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/axstin/rbxfpsunlocker/blob/master/LICENSE">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="14" Text="rbxfpsunlocker by axstin" />
|
|
||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="MIT License" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
|
||||||
</StackPanel>
|
|
||||||
</ui:CardAction>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ui:UiPage>
|
</ui:UiPage>
|
||||||
|
@ -43,8 +43,8 @@
|
|||||||
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
<ui:CardControl.Header>
|
<ui:CardControl.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontSize="14" Text="Prompt on Roblox-forced channel change" />
|
<TextBlock FontSize="14" Text="Prompt on automatic channel change" />
|
||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Bloxstrap will ask you if you want to change the release channel to what Roblox mandates." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Bloxstrap may automatically change your preferred release channel. Enabling will ask you before changing." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ui:CardControl.Header>
|
</ui:CardControl.Header>
|
||||||
<ui:ToggleSwitch IsChecked="{Binding ChannelChangePromptingEnabled, Mode=TwoWay}" />
|
<ui:ToggleSwitch IsChecked="{Binding ChannelChangePromptingEnabled, Mode=TwoWay}" />
|
||||||
|
@ -59,7 +59,8 @@
|
|||||||
<TextBlock FontSize="14" Text="Channel" />
|
<TextBlock FontSize="14" Text="Channel" />
|
||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Choose which release channel to download Roblox from." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Choose which release channel to download Roblox from." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ComboBox Grid.Column="1" Margin="8,0,8,0" Padding="10,5,10,5" Width="200" ItemsSource="{Binding Channels, Mode=OneWay}" Text="{Binding Channel, Mode=TwoWay}" />
|
<ComboBox Grid.Column="1" Margin="8,0,8,0" Padding="10,5,10,5" Width="200" ItemsSource="{Binding Channels, Mode=OneWay}" Text="{Binding Channel, Mode=TwoWay}" Visibility="{Binding ChannelComboBoxVisibility, Mode=OneWay}" />
|
||||||
|
<ui:TextBox Grid.Column="1" Margin="8,0,8,0" Padding="10,5,10,5" Width="200" Text="{Binding Channel, Mode=TwoWay}" Visibility="{Binding ChannelTextBoxVisibility, Mode=OneWay}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ui:CardExpander.Header>
|
</ui:CardExpander.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@ -114,10 +115,10 @@
|
|||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<ui:ProgressRing Grid.Column="0" Margin="6" IsIndeterminate="True" />
|
<ui:ProgressRing Grid.Column="0" Margin="6" IsIndeterminate="True" />
|
||||||
<TextBlock Grid.Column="1" Margin="16" VerticalAlignment="Center" Text="Fetching latest deploy info, please wait..." />
|
<TextBlock Grid.Column="1" Margin="16" VerticalAlignment="Center" Text="{Binding ChannelInfoLoadingText, Mode=OneWay}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<CheckBox Grid.Column="1" Margin="4,0,0,0" Content="Show all available channels" VerticalAlignment="Top" IsChecked="{Binding ShowAllChannels, Mode=TwoWay}" />
|
<CheckBox Grid.Column="1" Margin="4,0,0,0" Content="Manually enter channel name" VerticalAlignment="Top" IsChecked="{Binding ManualChannelEntry, Mode=TwoWay}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ui:CardExpander>
|
</ui:CardExpander>
|
||||||
|
@ -34,6 +34,15 @@
|
|||||||
</ui:CardControl>
|
</ui:CardControl>
|
||||||
|
|
||||||
<TextBlock Text="ReShade" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
<TextBlock Text="ReShade" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
||||||
|
<Grid Margin="0,4,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ui:SymbolIcon Grid.Column="0" Margin="0,0,4,0" VerticalAlignment="Center" Symbol="Warning28" FontSize="18" Foreground="{DynamicResource PaletteAmberBrush}" />
|
||||||
|
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="Important Notice" FontSize="14" />
|
||||||
|
</Grid>
|
||||||
|
<TextBlock Margin="0,4,0,0" Text="As of April 18th, Roblox has started rolling out an update with 64-bit support and the Byfron anticheat. ReShade will not work with this update. If you wish to use ReShade, you will be forced to use the 32-bit version of Roblox that's still currently available. If you wish to use ZWinPlayer64 for 64-bit, you must disable ReShade. This is only temporary, as Roblox may soon merge this update into LIVE." TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||||
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
<ui:CardControl.Header>
|
<ui:CardControl.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@ -93,28 +102,6 @@
|
|||||||
</ui:CardAction>
|
</ui:CardAction>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel x:Name="RbxFpsUnlockerOptions">
|
|
||||||
<TextBlock Text="FPS Unlocking" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
|
||||||
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
|
||||||
<ui:CardControl.Header>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="14" Text="Use axstin's rbxfpsunlocker" />
|
|
||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="rbxfpsunlocker removes Roblox's 60FPS cap and will launch alongside Roblox." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
|
||||||
</StackPanel>
|
|
||||||
</ui:CardControl.Header>
|
|
||||||
<ui:ToggleSwitch x:Name="RbxFpsUnlockerEnabledToggle" IsChecked="{Binding RbxFpsUnlockerEnabled, Mode=TwoWay}" />
|
|
||||||
</ui:CardControl>
|
|
||||||
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12" IsEnabled="{Binding IsChecked, ElementName=RbxFpsUnlockerEnabledToggle, Mode=OneWay}">
|
|
||||||
<ui:CardControl.Header>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="14" Text="Auto close when Roblox closes" />
|
|
||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="rbxfpsunlocker will automatically close when Roblox closes." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
|
||||||
</StackPanel>
|
|
||||||
</ui:CardControl.Header>
|
|
||||||
<ui:ToggleSwitch IsChecked="{Binding RbxFpsUnlockerAutocloseEnabled, Mode=TwoWay}" />
|
|
||||||
</ui:CardControl>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<TextBlock Text="Miscellaneous" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
<TextBlock Text="Miscellaneous" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
||||||
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
<ui:CardControl Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
<ui:CardControl.Header>
|
<ui:CardControl.Header>
|
||||||
@ -127,7 +114,7 @@
|
|||||||
</ui:CardControl>
|
</ui:CardControl>
|
||||||
|
|
||||||
<TextBlock Text="Custom Integrations" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
<TextBlock Text="Custom Integrations" FontSize="16" FontWeight="Medium" Margin="0,16,0,0" />
|
||||||
<TextBlock Margin="0,4,0,0" Text="Here, you can have other programs launch with Roblox automatically like how rbxfpsunlocker does." TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
<TextBlock Margin="0,4,0,0" Text="Here, you can have other programs launch with Roblox automatically." TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||||
<Grid Margin="0,8,0,0">
|
<Grid Margin="0,8,0,0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
|
@ -15,10 +15,6 @@ namespace Bloxstrap.Views.Pages
|
|||||||
{
|
{
|
||||||
DataContext = new IntegrationsViewModel();
|
DataContext = new IntegrationsViewModel();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
// rbxfpsunlocker does not have 64 bit support
|
|
||||||
if (!Environment.Is64BitOperatingSystem)
|
|
||||||
this.RbxFpsUnlockerOptions.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CustomIntegrationSelection(object sender, SelectionChangedEventArgs e)
|
public void CustomIntegrationSelection(object sender, SelectionChangedEventArgs e)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
xmlns:models="clr-namespace:Bloxstrap.ViewModels"
|
xmlns:models="clr-namespace:Bloxstrap.ViewModels"
|
||||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="800" d:DesignWidth="800"
|
||||||
Title="ModsPage"
|
Title="ModsPage"
|
||||||
Scrollable="True">
|
Scrollable="True">
|
||||||
<StackPanel Margin="0,0,14,14">
|
<StackPanel Margin="0,0,14,14">
|
||||||
@ -99,16 +99,19 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<ui:CardControl Grid.Row="0" Margin="0,8,0,0" Padding="16,13,16,12">
|
<ui:CardControl Grid.Row="0" Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
<ui:CardControl.Header>
|
<ui:CardControl.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontSize="14" Text="Use exclusive fullscreen" />
|
<TextBlock FontSize="14" Text="Framerate limit" />
|
||||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Enables using Alt + Enter to enter exclusive fullscreen. Only works with Direct3D 11." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="By default, it's 60FPS. Use a really high number like 9999 for no limit." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ui:CardControl.Header>
|
</ui:CardControl.Header>
|
||||||
<ui:ToggleSwitch IsChecked="{Binding ExclusiveFullscreenEnabled, Mode=TwoWay}" />
|
<ui:TextBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" Text="{Binding FramerateLimit, Mode=TwoWay}" PreviewTextInput="ValidateInt32" />
|
||||||
</ui:CardControl>
|
</ui:CardControl>
|
||||||
<ui:CardControl Grid.Row="1" Margin="0,8,0,0" Padding="16,13,16,12">
|
<ui:CardControl Grid.Row="1" Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
<ui:CardControl.Header>
|
<ui:CardControl.Header>
|
||||||
@ -119,6 +122,33 @@
|
|||||||
</ui:CardControl.Header>
|
</ui:CardControl.Header>
|
||||||
<ComboBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" ItemsSource="{Binding RenderingModes.Keys, Mode=OneTime}" Text="{Binding SelectedRenderingMode, Mode=TwoWay}" />
|
<ComboBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" ItemsSource="{Binding RenderingModes.Keys, Mode=OneTime}" Text="{Binding SelectedRenderingMode, Mode=TwoWay}" />
|
||||||
</ui:CardControl>
|
</ui:CardControl>
|
||||||
|
<ui:CardControl Grid.Row="2" Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
|
<ui:CardControl.Header>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="14" Text="Use exclusive fullscreen" />
|
||||||
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Enables using Alt + Enter to enter exclusive fullscreen. Only works with Direct3D 11." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ui:CardControl.Header>
|
||||||
|
<ui:ToggleSwitch IsChecked="{Binding ExclusiveFullscreenEnabled, Mode=TwoWay}" />
|
||||||
|
</ui:CardControl>
|
||||||
|
<ui:CardControl Grid.Row="3" Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
|
<ui:CardControl.Header>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="14" Text="Use alternate graphics quality selector" />
|
||||||
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Toggle between using the consolidated 1-10 / fine-grained 1-21 graphics quality slider." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ui:CardControl.Header>
|
||||||
|
<ui:ToggleSwitch IsChecked="{Binding AlternateGraphicsSelectorEnabled, Mode=TwoWay}" />
|
||||||
|
</ui:CardControl>
|
||||||
|
<ui:CardControl Grid.Row="4" Margin="0,8,0,0" Padding="16,13,16,12">
|
||||||
|
<ui:CardControl.Header>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="14" Text="In-game menu version" />
|
||||||
|
<TextBlock Margin="0,2,0,0" FontSize="12" Text="Choose which version of the in-game menu to use. Current default is v1." Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ui:CardControl.Header>
|
||||||
|
<ComboBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" ItemsSource="{Binding IGMenuVersions.Keys, Mode=OneTime}" Text="{Binding SelectedIGMenuVersion, Mode=TwoWay}" />
|
||||||
|
</ui:CardControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel x:Name="MiscellaneousOptions">
|
<StackPanel x:Name="MiscellaneousOptions">
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
using Bloxstrap.ViewModels;
|
using Bloxstrap.ViewModels;
|
||||||
|
|
||||||
namespace Bloxstrap.Views.Pages
|
namespace Bloxstrap.Views.Pages
|
||||||
@ -11,9 +13,6 @@ namespace Bloxstrap.Views.Pages
|
|||||||
{
|
{
|
||||||
public ModsPage()
|
public ModsPage()
|
||||||
{
|
{
|
||||||
if (!App.IsFirstRun)
|
|
||||||
App.FastFlags.Load();
|
|
||||||
|
|
||||||
DataContext = new ModsViewModel();
|
DataContext = new ModsViewModel();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@ -21,5 +20,7 @@ namespace Bloxstrap.Views.Pages
|
|||||||
if (Environment.OSVersion.Version.Build < 17093)
|
if (Environment.OSVersion.Version.Build < 17093)
|
||||||
this.MiscellaneousOptions.Visibility = Visibility.Collapsed;
|
this.MiscellaneousOptions.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ValidateInt32(object sender, TextCompositionEventArgs e) => e.Handled = !Int32.TryParse(e.Text, out int _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
README.md
12
README.md
@ -22,7 +22,7 @@ Alternatively, you can install Bloxstrap via [Winget](https://winstall.app/apps/
|
|||||||
> winget install bloxstrap
|
> winget install bloxstrap
|
||||||
```
|
```
|
||||||
|
|
||||||
You will also need the [.NET 6 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.14-windows-x64-installer). If you don't already have it installed, you'll be prompted to install it anyway. Be sure to install Bloxstrap after you've installed this.
|
You will also need the [.NET 6 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.16-windows-x64-installer). If you don't already have it installed, you'll be prompted to install it anyway. Be sure to install Bloxstrap after you've installed this.
|
||||||
|
|
||||||
It's not unlikely that Windows Smartscreen will show a popup when you run Bloxstrap for the first time. This happens because it's an unknown program, not because it's actually detected as being malicious. To dismiss it, just click on "More info" and then "Run anyway".
|
It's not unlikely that Windows Smartscreen will show a popup when you run Bloxstrap for the first time. This happens because it's an unknown program, not because it's actually detected as being malicious. To dismiss it, just click on "More info" and then "Run anyway".
|
||||||
|
|
||||||
@ -36,12 +36,12 @@ Here's some of the features that Bloxstrap provides over the stock Roblox bootst
|
|||||||
* Persistent file modifications - re-adds the old death sound!
|
* Persistent file modifications - re-adds the old death sound!
|
||||||
* Support for shaders with [ReShade](https://reshade.me) and [Extravi's ReShade Presets](https://bloxshade.com/)
|
* Support for shaders with [ReShade](https://reshade.me) and [Extravi's ReShade Presets](https://bloxshade.com/)
|
||||||
* Painless support for Discord Rich Presence - no auth cookie needed!
|
* Painless support for Discord Rich Presence - no auth cookie needed!
|
||||||
* Automatic silent FPS unlocking with [rbxfpsunlocker](https://github.com/axstin/rbxfpsunlocker)
|
* Built-in FPS unlocking
|
||||||
* Ability to disable the Roblox desktop app
|
|
||||||
* A customizable launcher look - includes dark theme!
|
* A customizable launcher look - includes dark theme!
|
||||||
* Ability to opt into non-production Roblox release channels
|
* Lets you disable the Roblox desktop app
|
||||||
* Ability to see what region your current server is located in
|
* Lets you opt into non-production Roblox release channels
|
||||||
* Support for having multiple Roblox game instances open simultaneously
|
* Lets you see what region your current server is located in
|
||||||
|
* Lets you have multiple Roblox game instances open simultaneously
|
||||||
|
|
||||||
All the available features are browsable through the Bloxstrap menu. There's not too many, but it's recommended to look through all of them.
|
All the available features are browsable through the Bloxstrap menu. There's not too many, but it's recommended to look through all of them.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user