@@ -38,6 +38,7 @@ public class GameTaskPlugin : GenericPlugin
3838
3939 // Cooldown: tracks last launch time to prevent double-launch
4040 private DateTime lastLaunchTime = DateTime . MinValue ;
41+ private const int LaunchCooldownMs = 3000 ;
4142
4243 public GameTaskPlugin ( IPlayniteAPI api ) : base ( api )
4344 {
@@ -84,6 +85,7 @@ public override void OnApplicationStarted(OnApplicationStartedEventArgs args)
8485 TrimLog ( Path . Combine ( pluginDataPath , "Logs" , "PS1.log" ) , maxBytes : 1024 * 1024 ) ;
8586
8687 // Run startup tasks in background to avoid blocking Playnite UI
88+ // and prevent the "serious error" crash on slow PCs with many tagged games
8789 System . Threading . Tasks . Task . Run ( ( ) =>
8890 {
8991 try
@@ -96,6 +98,7 @@ public override void OnApplicationStarted(OnApplicationStartedEventArgs args)
9698 if ( settingsManager . Current . DetectCorruptedTasks )
9799 CheckForCorruptedTasks ( ) ;
98100
101+ // ShowPendingNotification must run on UI thread
99102 api . MainView . UIDispatcher . Invoke ( ( ) =>
100103 notificationManager . ShowPendingNotification ( ) ) ;
101104 }
@@ -106,6 +109,10 @@ public override void OnApplicationStarted(OnApplicationStartedEventArgs args)
106109 } ) ;
107110 }
108111
112+ // =====================================================
113+ // LIBRARY SCAN
114+ // =====================================================
115+
109116 private void TrimLog ( string logPath , long maxBytes )
110117 {
111118 try
@@ -114,9 +121,9 @@ private void TrimLog(string logPath, long maxBytes)
114121 var info = new FileInfo ( logPath ) ;
115122 if ( info . Length <= maxBytes ) return ;
116123
117- var content = File . ReadAllText ( logPath ) ;
118- var trimmed = content . Substring ( content . Length / 2 ) ;
119- var firstLine = trimmed . IndexOf ( '\n ' ) ;
124+ var content = File . ReadAllText ( logPath ) ;
125+ var trimmed = content . Substring ( content . Length / 2 ) ;
126+ var firstLine = trimmed . IndexOf ( '\n ' ) ;
120127 if ( firstLine >= 0 ) trimmed = trimmed . Substring ( firstLine + 1 ) ;
121128
122129 File . WriteAllText ( logPath ,
@@ -127,10 +134,6 @@ private void TrimLog(string logPath, long maxBytes)
127134 catch ( Exception ex ) { logger . Log ( $ "ERROR trimming log: { ex . Message } ") ; }
128135 }
129136
130- // =====================================================
131- // LIBRARY SCAN
132- // =====================================================
133-
134137 private void ScanLibrary ( )
135138 {
136139 taskManager . ResetPendingFile ( ) ;
@@ -398,31 +401,25 @@ private string ResolveExePathForGame(Game game)
398401 if ( ! string . IsNullOrWhiteSpace ( customExe ) && File . Exists ( customExe ) )
399402 return customExe ;
400403
401- // 2. Check GameActions
402404 var action = game . GameActions ? . FirstOrDefault ( a =>
403405 a != null && a . Name != ActionName && ! string . IsNullOrWhiteSpace ( a . Path ) ) ;
404406
405- if ( action != null )
406- {
407- // 2a. Steam URL actions (steam://run/APPID) — resolve via steam.exe
408- if ( action . Path . StartsWith ( "steam://" , StringComparison . OrdinalIgnoreCase ) )
409- {
410- string steamExe = ResolveSteamExe ( ) ;
411- if ( ! string . IsNullOrWhiteSpace ( steamExe ) )
412- return steamExe ;
413- }
407+ if ( action == null ) return null ;
414408
415- // 2b. Regular exe action
416- string resolved = taskManager . ResolveExecutable ( game , action ) ;
417- if ( File . Exists ( resolved ) ) return resolved ;
409+ // 2. Steam URL actions — resolve via steam.exe
410+ if ( action . Path . StartsWith ( "steam://" , StringComparison . OrdinalIgnoreCase ) )
411+ {
412+ string steamExe = ResolveSteamExe ( ) ;
413+ if ( ! string . IsNullOrWhiteSpace ( steamExe ) ) return steamExe ;
418414 }
419415
420- return null ;
416+ // 3. Regular exe action
417+ string resolved = taskManager . ResolveExecutable ( game , action ) ;
418+ return File . Exists ( resolved ) ? resolved : null ;
421419 }
422420
423421 private string ResolveSteamExe ( )
424422 {
425- // Common Steam installation paths
426423 string [ ] candidates =
427424 {
428425 @"C:\Program Files (x86)\Steam\steam.exe" ,
@@ -432,7 +429,6 @@ private string ResolveSteamExe()
432429 foreach ( var path in candidates )
433430 if ( File . Exists ( path ) ) return path ;
434431
435- // Try registry
436432 try
437433 {
438434 using var key = Microsoft . Win32 . Registry . LocalMachine
@@ -797,10 +793,28 @@ private void RunDeleteTasks()
797793
798794 public string ResolveExePathPublic ( Game game ) => ResolveExePathForGame ( game ) ;
799795
796+ public bool HasNoGameAction ( Game game )
797+ {
798+ if ( game . GameActions == null || ! game . GameActions . Any ( a =>
799+ a != null && a . Name != ActionName && ! string . IsNullOrWhiteSpace ( a . Path ) ) )
800+ return true ;
801+ return false ;
802+ }
803+
800804 public void InvokeFixAllUnknownExecutables ( ) => FixAllUnknownExecutables ( ) ;
801805
802806 public void InvokeRepairAll ( ) => RepairAll ( ) ;
803807
808+ public void InvokeRepairGame ( Game game )
809+ {
810+ RepairGameTask ( game ) ;
811+ notificationManager . ShowPendingNotification ( ) ;
812+ }
813+
814+ public void InvokeFixExecutablePath ( Game game ) => FixExecutablePath ( game ) ;
815+
816+ public void InvokeDisableGame ( Game game ) => DisableGameTask ( new [ ] { game } ) ;
817+
804818 // =====================================================
805819 // OPEN DIAGNOSTICS
806820 // =====================================================
0 commit comments