create launchsettings

This commit is contained in:
bluepilledgreat 2024-02-02 15:04:21 +00:00
parent 27309e6394
commit 09fc1dc031
6 changed files with 290 additions and 178 deletions

View File

@ -27,12 +27,7 @@ namespace Bloxstrap
public static bool IsSetupComplete { get; set; } = true;
public static bool IsFirstRun { get; set; } = true;
public static bool IsQuiet { get; private set; } = false;
public static bool IsUninstall { get; private set; } = false;
public static bool IsNoLaunch { get; private set; } = false;
public static bool IsUpgrade { get; private set; } = false;
public static bool IsMenuLaunch { get; private set; } = false;
public static string[] LaunchArgs { get; private set; } = null!;
public static LaunchSettings LaunchSettings { get; private set; } = null!;
public static BuildMetadataAttribute BuildMetadata = Assembly.GetExecutingAssembly().GetCustomAttribute<BuildMetadataAttribute>()!;
public static string Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2];
@ -103,6 +98,15 @@ namespace Bloxstrap
#endif
}
private void StartupFinished()
{
const string LOG_IDENT = "App::StartupFinished";
Logger.WriteLine(LOG_IDENT, "Successfully reached end of main thread. Terminating...");
Terminate();
}
protected override void OnStartup(StartupEventArgs e)
{
const string LOG_IDENT = "App::OnStartup";
@ -122,47 +126,10 @@ namespace Bloxstrap
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
LaunchArgs = e.Args;
#if DEBUG
Logger.WriteLine(LOG_IDENT, $"Arguments: {string.Join(' ', LaunchArgs)}");
#endif
LaunchSettings = new LaunchSettings(e.Args);
HttpClient.Timeout = TimeSpan.FromSeconds(30);
HttpClient.DefaultRequestHeaders.Add("User-Agent", ProjectRepository);
if (LaunchArgs.Length > 0)
{
if (Array.IndexOf(LaunchArgs, "-preferences") != -1 || Array.IndexOf(LaunchArgs, "-menu") != -1)
{
Logger.WriteLine(LOG_IDENT, "Started with IsMenuLaunch flag");
IsMenuLaunch = true;
}
if (Array.IndexOf(LaunchArgs, "-quiet") != -1)
{
Logger.WriteLine(LOG_IDENT, "Started with IsQuiet flag");
IsQuiet = true;
}
if (Array.IndexOf(LaunchArgs, "-uninstall") != -1)
{
Logger.WriteLine(LOG_IDENT, "Started with IsUninstall flag");
IsUninstall = true;
}
if (Array.IndexOf(LaunchArgs, "-nolaunch") != -1)
{
Logger.WriteLine(LOG_IDENT, "Started with IsNoLaunch flag");
IsNoLaunch = true;
}
if (Array.IndexOf(LaunchArgs, "-upgrade") != -1)
{
Logger.WriteLine(LOG_IDENT, "Bloxstrap started with IsUpgrade flag");
IsUpgrade = true;
}
}
using (var checker = new InstallChecker())
{
@ -175,7 +142,7 @@ namespace Bloxstrap
// just in case the user decides to cancel the install
if (!IsFirstRun)
{
Logger.Initialize(IsUninstall);
Logger.Initialize(LaunchSettings.IsUninstall);
if (!Logger.Initialized)
{
@ -188,7 +155,7 @@ namespace Bloxstrap
FastFlags.Load();
}
if (!IsUninstall && !IsMenuLaunch)
if (!LaunchSettings.IsUninstall && !LaunchSettings.IsMenuLaunch)
NotifyIcon = new();
#if !DEBUG
@ -196,10 +163,7 @@ namespace Bloxstrap
InstallChecker.CheckUpgrade();
#endif
string commandLine = "";
LaunchMode? launchMode = null;
if (IsMenuLaunch)
if (LaunchSettings.IsMenuLaunch)
{
Process? menuProcess = Process.GetProcesses().Where(x => x.MainWindowTitle == $"{ProjectName} Menu").FirstOrDefault();
@ -211,7 +175,7 @@ namespace Bloxstrap
}
else
{
if (Process.GetProcessesByName(ProjectName).Length > 1 && !IsQuiet)
if (Process.GetProcessesByName(ProjectName).Length > 1 && !LaunchSettings.IsQuiet)
Frontend.ShowMessageBox(
Bloxstrap.Resources.Strings.Menu_AlreadyRunning,
MessageBoxImage.Information
@ -219,152 +183,95 @@ namespace Bloxstrap
Frontend.ShowMenu();
}
}
else if (LaunchArgs.Length > 0)
{
if (LaunchArgs[0].StartsWith("roblox-player:"))
{
commandLine = ProtocolHandler.ParseUri(LaunchArgs[0]);
launchMode = LaunchMode.Player;
}
else if (LaunchArgs[0].StartsWith("roblox:"))
{
if (Settings.Prop.UseDisableAppPatch)
Frontend.ShowMessageBox(
Bloxstrap.Resources.Strings.Bootstrapper_DeeplinkTempEnabled,
MessageBoxImage.Information
);
commandLine = $"--app --deeplink {LaunchArgs[0]}";
launchMode = LaunchMode.Player;
}
else if (LaunchArgs[0].StartsWith("roblox-studio:"))
{
commandLine = ProtocolHandler.ParseUri(LaunchArgs[0]);
if (!commandLine.Contains("-startEvent"))
commandLine += " -startEvent www.roblox.com/robloxQTStudioStartedEvent";
launchMode = LaunchMode.Studio;
}
else if (LaunchArgs[0].StartsWith("roblox-studio-auth:"))
{
commandLine = HttpUtility.UrlDecode(LaunchArgs[0]);
launchMode = LaunchMode.StudioAuth;
}
else if (LaunchArgs[0] == "-ide")
{
launchMode = LaunchMode.Studio;
if (LaunchArgs.Length >= 2)
commandLine = $"-task EditFile -localPlaceFile \"{LaunchArgs[1]}\"";
}
else
{
commandLine = "--app";
launchMode = LaunchMode.Player;
}
}
else
{
commandLine = "--app";
launchMode = LaunchMode.Player;
StartupFinished();
return;
}
if (launchMode != null)
if (!IsFirstRun)
ShouldSaveConfigs = true;
// start bootstrapper and show the bootstrapper modal if we're not running silently
Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper");
Bootstrapper bootstrapper = new(LaunchSettings.RobloxLaunchArgs, LaunchSettings.RobloxLaunchMode);
IBootstrapperDialog? dialog = null;
if (!LaunchSettings.IsQuiet)
{
if (!IsFirstRun)
ShouldSaveConfigs = true;
// start bootstrapper and show the bootstrapper modal if we're not running silently
Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper");
Bootstrapper bootstrapper = new(commandLine, (LaunchMode)launchMode);
IBootstrapperDialog? dialog = null;
Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper dialog");
dialog = Settings.Prop.BootstrapperStyle.GetNew();
bootstrapper.Dialog = dialog;
dialog.Bootstrapper = bootstrapper;
}
if (!IsQuiet)
// handle roblox singleton mutex for multi-instance launching
// note we're handling it here in the main thread and NOT in the
// bootstrapper as handling mutexes in async contexts suuuuuucks
Mutex? singletonMutex = null;
if (Settings.Prop.MultiInstanceLaunching && LaunchSettings.RobloxLaunchMode == LaunchMode.Player)
{
Logger.WriteLine(LOG_IDENT, "Creating singleton mutex");
try
{
Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper dialog");
dialog = Settings.Prop.BootstrapperStyle.GetNew();
bootstrapper.Dialog = dialog;
dialog.Bootstrapper = bootstrapper;
Mutex.OpenExisting("ROBLOX_singletonMutex");
Logger.WriteLine(LOG_IDENT, "Warning - singleton mutex already exists!");
}
// handle roblox singleton mutex for multi-instance launching
// note we're handling it here in the main thread and NOT in the
// bootstrapper as handling mutexes in async contexts suuuuuucks
Mutex? singletonMutex = null;
if (Settings.Prop.MultiInstanceLaunching && launchMode == LaunchMode.Player)
catch
{
Logger.WriteLine(LOG_IDENT, "Creating singleton mutex");
try
{
Mutex.OpenExisting("ROBLOX_singletonMutex");
Logger.WriteLine(LOG_IDENT, "Warning - singleton mutex already exists!");
}
catch
{
// create the singleton mutex before the game client does
singletonMutex = new Mutex(true, "ROBLOX_singletonMutex");
}
// create the singleton mutex before the game client does
singletonMutex = new Mutex(true, "ROBLOX_singletonMutex");
}
}
Task bootstrapperTask = Task.Run(async () => await bootstrapper.Run()).ContinueWith(t =>
{
Logger.WriteLine(LOG_IDENT, "Bootstrapper task has finished");
Task bootstrapperTask = Task.Run(async () => await bootstrapper.Run()).ContinueWith(t =>
{
Logger.WriteLine(LOG_IDENT, "Bootstrapper task has finished");
// notifyicon is blocking main thread, must be disposed here
NotifyIcon?.Dispose();
// notifyicon is blocking main thread, must be disposed here
NotifyIcon?.Dispose();
if (t.IsFaulted)
Logger.WriteLine(LOG_IDENT, "An exception occurred when running the bootstrapper");
if (t.IsFaulted)
Logger.WriteLine(LOG_IDENT, "An exception occurred when running the bootstrapper");
if (t.Exception is null)
return;
if (t.Exception is null)
return;
Logger.WriteException(LOG_IDENT, t.Exception);
Logger.WriteException(LOG_IDENT, t.Exception);
Exception exception = t.Exception;
Exception exception = t.Exception;
#if !DEBUG
if (t.Exception.GetType().ToString() == "System.AggregateException")
if (t.Exception.GetType().ToString() == "System.AggregateException")
exception = t.Exception.InnerException!;
#endif
FinalizeExceptionHandling(exception, false);
});
FinalizeExceptionHandling(exception, false);
});
// this ordering is very important as all wpf windows are shown as modal dialogs, mess it up and you'll end up blocking input to one of them
dialog?.ShowBootstrapper();
// this ordering is very important as all wpf windows are shown as modal dialogs, mess it up and you'll end up blocking input to one of them
dialog?.ShowBootstrapper();
if (!IsNoLaunch && Settings.Prop.EnableActivityTracking)
NotifyIcon?.InitializeContextMenu();
if (!LaunchSettings.IsNoLaunch && Settings.Prop.EnableActivityTracking)
NotifyIcon?.InitializeContextMenu();
Logger.WriteLine(LOG_IDENT, "Waiting for bootstrapper task to finish");
Logger.WriteLine(LOG_IDENT, "Waiting for bootstrapper task to finish");
bootstrapperTask.Wait();
bootstrapperTask.Wait();
if (singletonMutex is not null)
{
Logger.WriteLine(LOG_IDENT, "We have singleton mutex ownership! Running in background until all Roblox processes are closed");
if (singletonMutex is not null)
{
Logger.WriteLine(LOG_IDENT, "We have singleton mutex ownership! Running in background until all Roblox processes are closed");
// we've got ownership of the roblox singleton mutex!
// if we stop running, everything will screw up once any more roblox instances launched
while (Process.GetProcessesByName("RobloxPlayerBeta").Any())
Thread.Sleep(5000);
}
// we've got ownership of the roblox singleton mutex!
// if we stop running, everything will screw up once any more roblox instances launched
while (Process.GetProcessesByName("RobloxPlayerBeta").Any())
Thread.Sleep(5000);
}
Logger.WriteLine(LOG_IDENT, "Successfully reached end of main thread. Terminating...");
Terminate();
StartupFinished();
}
}
}

View File

@ -123,7 +123,7 @@ namespace Bloxstrap
App.Logger.WriteLine(LOG_IDENT, "Running bootstrapper");
if (App.IsUninstall)
if (App.LaunchSettings.IsUninstall)
{
Uninstall();
return;
@ -226,9 +226,9 @@ namespace Bloxstrap
await mutex.ReleaseAsync();
if (App.IsFirstRun && App.IsNoLaunch)
if (App.IsFirstRun && App.LaunchSettings.IsNoLaunch)
Dialog?.ShowSuccess(Resources.Strings.Bootstrapper_SuccessfullyInstalled);
else if (!App.IsNoLaunch && !_cancelFired)
else if (!App.LaunchSettings.IsNoLaunch && !_cancelFired)
await StartRoblox();
}
@ -302,7 +302,7 @@ namespace Bloxstrap
MessageBoxImage.Error
);
if (!App.IsQuiet)
if (!App.LaunchSettings.IsQuiet)
Utilities.ShellExecute("https://support.microsoft.com/en-us/topic/media-feature-pack-list-for-windows-n-editions-c1c6fffa-d052-8338-7a79-a4bb980a700a");
Dialog?.CloseBootstrapper();
@ -655,7 +655,7 @@ namespace Bloxstrap
FileName = downloadLocation,
};
foreach (string arg in App.LaunchArgs)
foreach (string arg in App.LaunchSettings.Args)
startInfo.ArgumentList.Add(arg);
App.Settings.Save();

View File

@ -124,7 +124,7 @@ namespace Bloxstrap
App.BaseDirectory = Path.Combine(Paths.LocalAppData, App.ProjectName);
App.Logger.Initialize(true);
if (App.IsQuiet)
if (App.LaunchSettings.IsQuiet)
return;
App.IsSetupComplete = false;
@ -159,7 +159,7 @@ namespace Bloxstrap
MessageBoxResult result;
// silently upgrade version if the command line flag is set or if we're launching from an auto update
if (App.IsUpgrade || isAutoUpgrade)
if (App.LaunchSettings.IsUpgrade || isAutoUpgrade)
{
result = MessageBoxResult.Yes;
}
@ -238,7 +238,7 @@ namespace Bloxstrap
(_, _) => Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/releases/tag/v{currentVersionInfo.ProductVersion}")
);
}
else if (!App.IsQuiet)
else if (!App.LaunchSettings.IsQuiet)
{
Frontend.ShowMessageBox(
string.Format(Resources.Strings.InstallChecker_Updated, currentVersionInfo.ProductVersion),

181
Bloxstrap/LaunchSettings.cs Normal file
View File

@ -0,0 +1,181 @@
using Bloxstrap.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
namespace Bloxstrap
{
public class LaunchSettings
{
[LaunchFlag(new[] { "-preferences", "-menu" })]
public bool IsMenuLaunch { get; private set; } = false;
[LaunchFlag("-quiet")]
public bool IsQuiet { get; private set; } = false;
[LaunchFlag("-uninstall")]
public bool IsUninstall { get; private set; } = false;
[LaunchFlag("-nolaunch")]
public bool IsNoLaunch { get; private set; } = false;
[LaunchFlag("-upgrade")]
public bool IsUpgrade { get; private set; } = false;
public LaunchMode RobloxLaunchMode { get; private set; } = LaunchMode.Player;
public string RobloxLaunchArgs { get; private set; } = "--app";
/// <summary>
/// Original launch arguments
/// </summary>
public string[] Args { get; private set; }
private Dictionary<string, PropertyInfo>? _flagMap;
// pizzaboxer wanted this
private void ParseLaunchFlagProps()
{
_flagMap = new Dictionary<string, PropertyInfo>();
foreach (var prop in typeof(LaunchSettings).GetProperties())
{
var attr = prop.GetCustomAttribute<LaunchFlagAttribute>();
if (attr == null)
continue;
if (!string.IsNullOrEmpty(attr.Name))
{
_flagMap[attr.Name] = prop;
}
else
{
foreach (var name in attr.Names!)
_flagMap[name] = prop;
}
}
}
private void ParseFlag(string arg)
{
const string LOG_IDENT = "LaunchSettings::ParseFlag";
arg = arg.ToLowerInvariant();
if (_flagMap!.ContainsKey(arg))
{
var prop = _flagMap[arg];
prop.SetValue(this, true);
App.Logger.WriteLine(LOG_IDENT, $"Started with {prop.Name} flag");
}
}
private void ParseRoblox(string arg, ref int i)
{
if (arg.StartsWith("roblox-player:"))
{
RobloxLaunchArgs = ProtocolHandler.ParseUri(arg);
RobloxLaunchMode = LaunchMode.Player;
}
else if (arg.StartsWith("roblox:"))
{
if (App.Settings.Prop.UseDisableAppPatch)
Frontend.ShowMessageBox(
Resources.Strings.Bootstrapper_DeeplinkTempEnabled,
MessageBoxImage.Information
);
RobloxLaunchArgs = $"--app --deeplink {arg}";
RobloxLaunchMode = LaunchMode.Player;
}
else if (arg.StartsWith("roblox-studio:"))
{
RobloxLaunchArgs = ProtocolHandler.ParseUri(arg);
if (!RobloxLaunchArgs.Contains("-startEvent"))
RobloxLaunchArgs += " -startEvent www.roblox.com/robloxQTStudioStartedEvent";
RobloxLaunchMode = LaunchMode.Studio;
}
else if (arg.StartsWith("roblox-studio-auth:"))
{
RobloxLaunchArgs = HttpUtility.UrlDecode(arg);
RobloxLaunchMode = LaunchMode.StudioAuth;
}
else if (arg == "-ide")
{
RobloxLaunchMode = LaunchMode.Studio;
if (Args.Length >= 2)
{
string pathArg = Args[i + 1];
if (pathArg.StartsWith('-'))
return; // likely a launch flag, ignore it.
i++; // path arg
RobloxLaunchArgs = $"-task EditFile -localPlaceFile \"{pathArg}\"";
}
}
}
private void Parse()
{
const string LOG_IDENT = "LaunchSettings::Parse";
App.Logger.WriteLine(LOG_IDENT, "Parsing launch arguments");
#if DEBUG
App.Logger.WriteLine(LOG_IDENT, $"Launch arguments: {string.Join(' ', Args)}");
#endif
if (Args.Length == 0)
{
App.Logger.WriteLine(LOG_IDENT, "No launch arguments to parse");
return;
}
int idx = 0;
string firstArg = Args[0];
// check & handle roblox arg
if (!firstArg.StartsWith('-') || firstArg == "-ide")
{
ParseRoblox(firstArg, ref idx);
idx++; // roblox arg
}
// check if there are any launch flags
if (idx > Args.Length - 1)
return;
App.Logger.WriteLine(LOG_IDENT, "Parsing launch flags");
// map out launch flags
ParseLaunchFlagProps();
// parse any launch flags
for (int i = idx; i < Args.Length; i++)
ParseFlag(Args[i]);
// cleanup flag map
_flagMap!.Clear();
_flagMap = null;
}
public LaunchSettings(string[] args)
{
Args = args;
Parse();
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bloxstrap.Models.Attributes
{
public class LaunchFlagAttribute : Attribute
{
public string? Name { get; private set; }
public string[]? Names { get; private set; }
public LaunchFlagAttribute(string name)
{
Name = name;
}
public LaunchFlagAttribute(string[] names)
{
Names = names;
}
}
}

View File

@ -12,7 +12,7 @@ namespace Bloxstrap.UI
public static MessageBoxResult ShowMessageBox(string message, MessageBoxImage icon = MessageBoxImage.None, MessageBoxButton buttons = MessageBoxButton.OK, MessageBoxResult defaultResult = MessageBoxResult.None)
{
if (App.IsQuiet)
if (App.LaunchSettings.IsQuiet)
return defaultResult;
switch (App.Settings.Prop.BootstrapperStyle)