@@ -313,11 +313,14 @@ public async Task Run()
313
313
314
314
if ( ! App . LaunchSettings . NoLaunchFlag . Active && ! _cancelTokenSource . IsCancellationRequested )
315
315
{
316
- // show some balloon tips
317
- if ( ! _packageExtractionSuccess )
318
- Frontend . ShowBalloonTip ( Strings . Bootstrapper_ExtractionFailed_Title , Strings . Bootstrapper_ExtractionFailed_Message , ToolTipIcon . Warning ) ;
319
- else if ( ! allModificationsApplied )
320
- Frontend . ShowBalloonTip ( Strings . Bootstrapper_ModificationsFailed_Title , Strings . Bootstrapper_ModificationsFailed_Message , ToolTipIcon . Warning ) ;
316
+ if ( ! App . LaunchSettings . QuietFlag . Active )
317
+ {
318
+ // show some balloon tips
319
+ if ( ! _packageExtractionSuccess )
320
+ Frontend . ShowBalloonTip ( Strings . Bootstrapper_ExtractionFailed_Title , Strings . Bootstrapper_ExtractionFailed_Message , ToolTipIcon . Warning ) ;
321
+ else if ( ! allModificationsApplied )
322
+ Frontend . ShowBalloonTip ( Strings . Bootstrapper_ModificationsFailed_Title , Strings . Bootstrapper_ModificationsFailed_Message , ToolTipIcon . Warning ) ;
323
+ }
321
324
322
325
StartRoblox ( ) ;
323
326
}
@@ -490,21 +493,48 @@ private bool IsEligibleForBackgroundUpdate()
490
493
}
491
494
}
492
495
496
+ private static void LaunchMultiInstanceWatcher ( )
497
+ {
498
+ const string LOG_IDENT = "Bootstrapper::LaunchMultiInstanceWatcher" ;
499
+
500
+ if ( Utilities . DoesMutexExist ( "ROBLOX_singletonMutex" ) )
501
+ {
502
+ App . Logger . WriteLine ( LOG_IDENT , "Roblox singleton mutex already exists" ) ;
503
+ return ;
504
+ }
505
+
506
+ using EventWaitHandle initEventHandle = new EventWaitHandle ( false , EventResetMode . AutoReset , "Bloxstrap-MultiInstanceWatcherInitialisationFinished" ) ;
507
+ Process . Start ( Paths . Process , "-multiinstancewatcher" ) ;
508
+
509
+ bool initSuccess = initEventHandle . WaitOne ( TimeSpan . FromSeconds ( 2 ) ) ;
510
+ if ( initSuccess )
511
+ App . Logger . WriteLine ( LOG_IDENT , "Initialisation finished signalled, continuing." ) ;
512
+ else
513
+ App . Logger . WriteLine ( LOG_IDENT , "Did not receive the initialisation finished signal, continuing." ) ;
514
+ }
515
+
493
516
private void StartRoblox ( )
494
517
{
495
518
const string LOG_IDENT = "Bootstrapper::StartRoblox" ;
496
519
497
520
SetStatus ( Strings . Bootstrapper_Status_Starting ) ;
498
521
499
- if ( _launchMode == LaunchMode . Player && App . Settings . Prop . ForceRobloxLanguage )
522
+ if ( _launchMode == LaunchMode . Player )
500
523
{
501
- var match = Regex . Match ( _launchCommandLine , "gameLocale:([a-z_]+)" , RegexOptions . CultureInvariant ) ;
524
+ // this needs to be done before roblox launches
525
+ if ( App . Settings . Prop . MultiInstanceLaunching )
526
+ LaunchMultiInstanceWatcher ( ) ;
527
+
528
+ if ( App . Settings . Prop . ForceRobloxLanguage )
529
+ {
530
+ var match = Regex . Match ( _launchCommandLine , "gameLocale:([a-z_]+)" , RegexOptions . CultureInvariant ) ;
502
531
503
- if ( match . Groups . Count == 2 )
504
- _launchCommandLine = _launchCommandLine . Replace (
505
- "robloxLocale:en_us" ,
506
- $ "robloxLocale:{ match . Groups [ 1 ] . Value } ",
507
- StringComparison . OrdinalIgnoreCase ) ;
532
+ if ( match . Groups . Count == 2 )
533
+ _launchCommandLine = _launchCommandLine . Replace (
534
+ "robloxLocale:en_us" ,
535
+ $ "robloxLocale:{ match . Groups [ 1 ] . Value } ",
536
+ StringComparison . OrdinalIgnoreCase ) ;
537
+ }
508
538
}
509
539
510
540
var startInfo = new ProcessStartInfo ( )
@@ -848,6 +878,12 @@ public static void CleanupVersionsFolder()
848
878
return ;
849
879
}
850
880
881
+ if ( ! Directory . Exists ( Paths . Versions ) )
882
+ {
883
+ App . Logger . WriteLine ( LOG_IDENT , "Versions directory does not exist, skipping cleanup." ) ;
884
+ return ;
885
+ }
886
+
851
887
foreach ( string dir in Directory . GetDirectories ( Paths . Versions ) )
852
888
{
853
889
string dirName = Path . GetFileName ( dir ) ;
@@ -866,7 +902,7 @@ public static void CleanupVersionsFolder()
866
902
{
867
903
Directory . Delete ( dir , true ) ;
868
904
}
869
- catch ( IOException ex )
905
+ catch ( Exception ex )
870
906
{
871
907
App . Logger . WriteLine ( LOG_IDENT , $ "Failed to delete { dir } ") ;
872
908
App . Logger . WriteException ( LOG_IDENT , ex ) ;
@@ -894,21 +930,19 @@ private void MigrateCompatibilityFlags()
894
930
}
895
931
}
896
932
897
- private void KillRunningRobloxInDirectory ( string path )
933
+ private static void KillRobloxPlayers ( )
898
934
{
899
- const string LOG_IDENT = "Bootstrapper::KillRunningRobloxInDirectory " ;
935
+ const string LOG_IDENT = "Bootstrapper::KillRobloxPlayers " ;
900
936
901
937
List < Process > processes = new List < Process > ( ) ;
902
- processes . AddRange ( Process . GetProcessesByName ( IsStudioLaunch ? "RobloxStudioBeta" : "RobloxPlayerBeta" ) ) ;
903
- processes . AddRange ( Process . GetProcessesByName ( "RobloxCrashHandler" ) ) ;
938
+ processes . AddRange ( Process . GetProcessesByName ( "RobloxPlayerBeta" ) ) ;
939
+ processes . AddRange ( Process . GetProcessesByName ( "RobloxCrashHandler" ) ) ; // roblox studio doesnt depend on crash handler being open, so this should be fine
904
940
905
941
foreach ( Process process in processes )
906
942
{
907
943
try
908
944
{
909
- string ? processPath = process . MainModule ? . FileName ;
910
- if ( processPath != null && processPath . StartsWith ( path ) )
911
- process . Kill ( ) ;
945
+ process . Kill ( ) ;
912
946
}
913
947
catch ( Exception ex )
914
948
{
@@ -1130,6 +1164,8 @@ private async Task UpgradeRoblox()
1130
1164
1131
1165
App . Logger . WriteLine ( LOG_IDENT , $ "Registered as { totalSize } KB") ;
1132
1166
1167
+ App . State . Prop . ForceReinstall = false ;
1168
+
1133
1169
App . State . Save ( ) ;
1134
1170
App . RobloxState . Save ( ) ;
1135
1171
0 commit comments