mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
reintroduce multi-instance launching
This commit is contained in:
parent
49fd8eb2d2
commit
4a1068713e
@ -471,13 +471,39 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void LaunchMultiInstanceWatcher()
|
||||||
|
{
|
||||||
|
const string LOG_IDENT = "Bootstrapper::LaunchMultiInstanceWatcher";
|
||||||
|
|
||||||
|
if (Utilities.DoesMutexExist("ROBLOX_singletonMutex"))
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Roblox singleton mutex already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using EventWaitHandle initEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "Bloxstrap-MultiInstanceWatcherInitialisationFinished");
|
||||||
|
Process.Start(Paths.Process, "-multiinstancewatcher");
|
||||||
|
|
||||||
|
bool initSuccess = initEventHandle.WaitOne(TimeSpan.FromSeconds(2));
|
||||||
|
if (initSuccess)
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Initialisation finished signalled, continuing.");
|
||||||
|
else
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Did not receive the initialisation finished signal, continuing.");
|
||||||
|
}
|
||||||
|
|
||||||
private void StartRoblox()
|
private void StartRoblox()
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "Bootstrapper::StartRoblox";
|
const string LOG_IDENT = "Bootstrapper::StartRoblox";
|
||||||
|
|
||||||
SetStatus(Strings.Bootstrapper_Status_Starting);
|
SetStatus(Strings.Bootstrapper_Status_Starting);
|
||||||
|
|
||||||
if (_launchMode == LaunchMode.Player && App.Settings.Prop.ForceRobloxLanguage)
|
if (_launchMode == LaunchMode.Player)
|
||||||
|
{
|
||||||
|
// this needs to be done before roblox launches
|
||||||
|
if (App.Settings.Prop.MultiInstanceLaunching)
|
||||||
|
LaunchMultiInstanceWatcher();
|
||||||
|
|
||||||
|
if (App.Settings.Prop.ForceRobloxLanguage)
|
||||||
{
|
{
|
||||||
var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant);
|
var match = Regex.Match(_launchCommandLine, "gameLocale:([a-z_]+)", RegexOptions.CultureInvariant);
|
||||||
|
|
||||||
@ -487,6 +513,7 @@ namespace Bloxstrap
|
|||||||
$"robloxLocale:{match.Groups[1].Value}",
|
$"robloxLocale:{match.Groups[1].Value}",
|
||||||
StringComparison.OrdinalIgnoreCase);
|
StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var startInfo = new ProcessStartInfo()
|
var startInfo = new ProcessStartInfo()
|
||||||
{
|
{
|
||||||
|
@ -59,6 +59,11 @@ namespace Bloxstrap
|
|||||||
App.Logger.WriteLine(LOG_IDENT, "Opening watcher");
|
App.Logger.WriteLine(LOG_IDENT, "Opening watcher");
|
||||||
LaunchWatcher();
|
LaunchWatcher();
|
||||||
}
|
}
|
||||||
|
else if (App.LaunchSettings.MultiInstanceWatcherFlag.Active)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Opening multi-instance watcher");
|
||||||
|
LaunchMultiInstanceWatcher();
|
||||||
|
}
|
||||||
else if (App.LaunchSettings.BackgroundUpdaterFlag.Active)
|
else if (App.LaunchSettings.BackgroundUpdaterFlag.Active)
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Opening background updater");
|
App.Logger.WriteLine(LOG_IDENT, "Opening background updater");
|
||||||
@ -223,7 +228,7 @@ namespace Bloxstrap
|
|||||||
App.Terminate(ErrorCode.ERROR_FILE_NOT_FOUND);
|
App.Terminate(ErrorCode.ERROR_FILE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App.Settings.Prop.ConfirmLaunches && Mutex.TryOpenExisting("ROBLOX_singletonMutex", out var _))
|
if (App.Settings.Prop.ConfirmLaunches && Mutex.TryOpenExisting("ROBLOX_singletonMutex", out var _) && !App.Settings.Prop.MultiInstanceLaunching)
|
||||||
{
|
{
|
||||||
// this currently doesn't work very well since it relies on checking the existence of the singleton mutex
|
// this currently doesn't work very well since it relies on checking the existence of the singleton mutex
|
||||||
// which often hangs around for a few seconds after the window closes
|
// which often hangs around for a few seconds after the window closes
|
||||||
@ -302,6 +307,28 @@ namespace Bloxstrap
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void LaunchMultiInstanceWatcher()
|
||||||
|
{
|
||||||
|
const string LOG_IDENT = "LaunchHandler::LaunchMultiInstanceWatcher";
|
||||||
|
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Starting multi-instance watcher");
|
||||||
|
|
||||||
|
Task.Run(MultiInstanceWatcher.Run).ContinueWith(t =>
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Multi instance watcher task has finished");
|
||||||
|
|
||||||
|
if (t.IsFaulted)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "An exception occurred when running the multi-instance watcher");
|
||||||
|
|
||||||
|
if (t.Exception is not null)
|
||||||
|
App.FinalizeExceptionHandling(t.Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
App.Terminate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void LaunchBackgroundUpdater()
|
public static void LaunchBackgroundUpdater()
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "LaunchHandler::LaunchBackgroundUpdater";
|
const string LOG_IDENT = "LaunchHandler::LaunchBackgroundUpdater";
|
||||||
|
@ -16,6 +16,8 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
public LaunchFlag WatcherFlag { get; } = new("watcher");
|
public LaunchFlag WatcherFlag { get; } = new("watcher");
|
||||||
|
|
||||||
|
public LaunchFlag MultiInstanceWatcherFlag { get; } = new("multiinstancewatcher");
|
||||||
|
|
||||||
public LaunchFlag BackgroundUpdaterFlag { get; } = new("backgroundupdater");
|
public LaunchFlag BackgroundUpdaterFlag { get; } = new("backgroundupdater");
|
||||||
|
|
||||||
public LaunchFlag QuietFlag { get; } = new("quiet");
|
public LaunchFlag QuietFlag { get; } = new("quiet");
|
||||||
|
@ -11,6 +11,7 @@ namespace Bloxstrap.Models.Persistable
|
|||||||
public string BootstrapperIconCustomLocation { get; set; } = "";
|
public string BootstrapperIconCustomLocation { get; set; } = "";
|
||||||
public Theme Theme { get; set; } = Theme.Default;
|
public Theme Theme { get; set; } = Theme.Default;
|
||||||
public bool CheckForUpdates { get; set; } = true;
|
public bool CheckForUpdates { get; set; } = true;
|
||||||
|
public bool MultiInstanceLaunching { get; set; } = false;
|
||||||
public bool ConfirmLaunches { get; set; } = false;
|
public bool ConfirmLaunches { get; set; } = false;
|
||||||
public string Locale { get; set; } = "nil";
|
public string Locale { get; set; } = "nil";
|
||||||
public bool ForceRobloxLanguage { get; set; } = false;
|
public bool ForceRobloxLanguage { get; set; } = false;
|
||||||
|
68
Bloxstrap/MultiInstanceWatcher.cs
Normal file
68
Bloxstrap/MultiInstanceWatcher.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
namespace Bloxstrap
|
||||||
|
{
|
||||||
|
internal static class MultiInstanceWatcher
|
||||||
|
{
|
||||||
|
private static int GetOpenProcessesCount()
|
||||||
|
{
|
||||||
|
const string LOG_IDENT = "MultiInstanceWatcher::GetOpenProcessesCount";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// prevent any possible race conditions by checking for bloxstrap processes too
|
||||||
|
int count = Process.GetProcesses().Count(x => x.ProcessName is "RobloxPlayerBeta" or "Bloxstrap");
|
||||||
|
count -= 1; // ignore the current process
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// everything process related can error at any time
|
||||||
|
App.Logger.WriteException(LOG_IDENT, ex);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FireInitialisedEvent()
|
||||||
|
{
|
||||||
|
using EventWaitHandle initEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "Bloxstrap-MultiInstanceWatcherInitialisationFinished");
|
||||||
|
initEventHandle.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run()
|
||||||
|
{
|
||||||
|
const string LOG_IDENT = "MultiInstanceWatcher::Run";
|
||||||
|
|
||||||
|
// try to get the mutex
|
||||||
|
bool acquiredMutex;
|
||||||
|
using Mutex mutex = new Mutex(false, "ROBLOX_singletonMutex");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
acquiredMutex = mutex.WaitOne(0);
|
||||||
|
}
|
||||||
|
catch (AbandonedMutexException)
|
||||||
|
{
|
||||||
|
acquiredMutex = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!acquiredMutex)
|
||||||
|
{
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Client singleton mutex is already acquired");
|
||||||
|
FireInitialisedEvent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "Acquired mutex!");
|
||||||
|
FireInitialisedEvent();
|
||||||
|
|
||||||
|
// watch for alive processes
|
||||||
|
int count;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
count = GetOpenProcessesCount();
|
||||||
|
}
|
||||||
|
while (count == -1 || count > 0); // redo if -1 (one of the Process apis failed)
|
||||||
|
|
||||||
|
App.Logger.WriteLine(LOG_IDENT, "All Roblox related processes have closed, exiting!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Bloxstrap/Resources/Strings.Designer.cs
generated
18
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -3425,6 +3425,24 @@ namespace Bloxstrap.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Allows for having more than one Roblox game client instance open simultaneously..
|
||||||
|
/// </summary>
|
||||||
|
public static string Menu_Integrations_MultiInstanceLaunching_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Menu.Integrations.MultiInstanceLaunching.Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Allow multi-instance launching.
|
||||||
|
/// </summary>
|
||||||
|
public static string Menu_Integrations_MultiInstanceLaunching_Title {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Menu.Integrations.MultiInstanceLaunching.Title", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to When in-game, you'll be able to see where your server is located via [ipinfo.io]({0})..
|
/// Looks up a localized string similar to When in-game, you'll be able to see where your server is located via [ipinfo.io]({0})..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1487,4 +1487,10 @@ Defaulting to {1}.</value>
|
|||||||
<value>Custom Theme {0}</value>
|
<value>Custom Theme {0}</value>
|
||||||
<comment>{0} is a string (e.g. '1', '1-1234')</comment>
|
<comment>{0} is a string (e.g. '1', '1-1234')</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Menu.Integrations.MultiInstanceLaunching.Title" xml:space="preserve">
|
||||||
|
<value>Allow multi-instance launching</value>
|
||||||
|
</data>
|
||||||
|
<data name="Menu.Integrations.MultiInstanceLaunching.Description" xml:space="preserve">
|
||||||
|
<value>Allows for having more than one Roblox game client instance open simultaneously.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -66,6 +66,14 @@
|
|||||||
<ui:ToggleSwitch IsChecked="{Binding DiscordAccountOnProfile, Mode=TwoWay}" />
|
<ui:ToggleSwitch IsChecked="{Binding DiscordAccountOnProfile, Mode=TwoWay}" />
|
||||||
</controls:OptionControl>
|
</controls:OptionControl>
|
||||||
|
|
||||||
|
<TextBlock Text="{x:Static resources:Strings.Common_Miscellaneous}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
|
||||||
|
|
||||||
|
<controls:OptionControl
|
||||||
|
Header="{x:Static resources:Strings.Menu_Integrations_MultiInstanceLaunching_Title}"
|
||||||
|
Description="{x:Static resources:Strings.Menu_Integrations_MultiInstanceLaunching_Description}">
|
||||||
|
<ui:ToggleSwitch IsChecked="{Binding MultiInstanceLaunchingEnabled, Mode=TwoWay}" />
|
||||||
|
</controls:OptionControl>
|
||||||
|
|
||||||
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_Custom_Title}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
|
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_Custom_Title}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
|
||||||
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_Custom_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_Custom_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||||
<Grid Margin="0,8,0,0">
|
<Grid Margin="0,8,0,0">
|
||||||
|
@ -125,6 +125,12 @@ namespace Bloxstrap.UI.ViewModels.Settings
|
|||||||
set => App.Settings.Prop.UseDisableAppPatch = value;
|
set => App.Settings.Prop.UseDisableAppPatch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool MultiInstanceLaunchingEnabled
|
||||||
|
{
|
||||||
|
get => App.Settings.Prop.MultiInstanceLaunching;
|
||||||
|
set => App.Settings.Prop.MultiInstanceLaunching = value;
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<CustomIntegration> CustomIntegrations
|
public ObservableCollection<CustomIntegration> CustomIntegrations
|
||||||
{
|
{
|
||||||
get => App.Settings.Prop.CustomIntegrations;
|
get => App.Settings.Prop.CustomIntegrations;
|
||||||
|
Loading…
Reference in New Issue
Block a user