@@ -491,7 +491,20 @@ public void StartTimeUpdates(IHubContext<Hubs.GameHub, IGameHubClient> hubContex
491491 {
492492 _timeUpdateTimer ? . Dispose ( ) ;
493493 _timeUpdateTimer = new System . Threading . Timer (
494- async _ => await BroadcastTimeUpdate ( hubContext ) ,
494+ async _ =>
495+ {
496+ try
497+ {
498+ await BroadcastTimeUpdate ( hubContext ) ;
499+ }
500+ catch ( Exception ex )
501+ {
502+ Console . WriteLine ( $ "[TIME DEBUG] CRITICAL ERROR: Game { Id } : Unhandled exception in time update loop - { ex . GetType ( ) . Name } : { ex . Message } ") ;
503+ Console . WriteLine ( $ "[TIME DEBUG] Stack trace: { ex . StackTrace } ") ;
504+ Console . WriteLine ( $ "[TIME DEBUG] Stopping time updates to prevent server crash.") ;
505+ StopTimeUpdates ( ) ;
506+ }
507+ } ,
495508 null ,
496509 TimeSpan . FromSeconds ( 1 ) ,
497510 TimeSpan . FromSeconds ( 1 ) ) ;
@@ -680,8 +693,26 @@ private int CalculatePipCount(CheckerColor color)
680693 /// </summary>
681694 private async Task BroadcastTimeUpdate ( IHubContext < Hubs . GameHub , IGameHubClient > hubContext )
682695 {
696+ // DEFENSIVE: Stop time updates if game is already over
697+ if ( Engine . GameOver )
698+ {
699+ Console . WriteLine ( $ "[TIME DEBUG] Game { Id } : Time update called but game is over. Stopping time updates.") ;
700+ StopTimeUpdates ( ) ;
701+ return ;
702+ }
703+
683704 if ( Engine . WhiteTimeState == null || Engine . RedTimeState == null || TimeControl == null )
684705 {
706+ Console . WriteLine ( $ "[TIME DEBUG] Game { Id } : Time update called but time state or time control is null. Stopping updates.") ;
707+ StopTimeUpdates ( ) ;
708+ return ;
709+ }
710+
711+ // DEFENSIVE: Don't run time updates for games without time control
712+ if ( TimeControl . Type == TimeControlType . None )
713+ {
714+ Console . WriteLine ( $ "[TIME DEBUG] Game { Id } : No time control enabled. Stopping time updates.") ;
715+ StopTimeUpdates ( ) ;
685716 return ;
686717 }
687718
@@ -730,19 +761,50 @@ private async Task BroadcastTimeUpdate(IHubContext<Hubs.GameHub, IGameHubClient>
730761 /// </summary>
731762 private async Task HandleTimeout ( IHubContext < Hubs . GameHub , IGameHubClient > hubContext )
732763 {
764+ // DEFENSIVE: Don't try to forfeit if game is already over
765+ if ( Engine . GameOver )
766+ {
767+ Console . WriteLine ( $ "[TIME DEBUG] Game { Id } : Timeout triggered but game already over. Skipping forfeit.") ;
768+ return ;
769+ }
770+
771+ // DEFENSIVE: Additional safety check for time control
772+ if ( TimeControl == null || TimeControl . Type == TimeControlType . None )
773+ {
774+ Console . WriteLine ( $ "[TIME DEBUG] Game { Id } : Timeout called but no time control enabled. Skipping forfeit.") ;
775+ return ;
776+ }
777+
733778 var losingPlayer = Engine . CurrentPlayer ;
734779 var winningPlayer = Engine . GetOpponent ( ) ;
735780
736781 if ( losingPlayer == null || winningPlayer == null )
737782 {
738- Console . WriteLine ( "[TIME DEBUG] ERROR: Cannot handle timeout - player is null" ) ;
783+ Console . WriteLine ( $ "[TIME DEBUG] ERROR: Game { Id } : Cannot handle timeout - player is null (CurrentPlayer: { losingPlayer ? . Color . ToString ( ) ?? "null" } , Opponent: { winningPlayer ? . Color . ToString ( ) ?? "null" } ) ") ;
739784 return ;
740785 }
741786
742- Console . WriteLine ( $ "[TIME DEBUG] TIMEOUT! { losingPlayer . Color } ran out of time. { winningPlayer . Color } wins! ") ;
787+ Console . WriteLine ( $ "[TIME DEBUG] Game { Id } : TIMEOUT! { losingPlayer . Color } ran out of time. { winningPlayer . Color } wins by forfeit. ") ;
743788
744- // Mark game as over using ForfeitGame
745- Engine . ForfeitGame ( winningPlayer ) ;
789+ try
790+ {
791+ // Mark game as over using ForfeitGame
792+ Engine . ForfeitGame ( winningPlayer ) ;
793+ }
794+ catch ( InvalidOperationException ex )
795+ {
796+ // Game state was invalid - log and return without crashing
797+ Console . WriteLine ( $ "[TIME DEBUG] ERROR: Game { Id } : Failed to forfeit game on timeout - { ex . Message } . Game may already be over or in invalid state.") ;
798+ StopTimeUpdates ( ) ;
799+ return ;
800+ }
801+ catch ( Exception ex )
802+ {
803+ // Unexpected exception - log and return without crashing the server
804+ Console . WriteLine ( $ "[TIME DEBUG] ERROR: Game { Id } : Unexpected error while handling timeout - { ex . GetType ( ) . Name } : { ex . Message } ") ;
805+ StopTimeUpdates ( ) ;
806+ return ;
807+ }
746808
747809 // Broadcast timeout event
748810 var timeoutEvent = new PlayerTimedOutDto
0 commit comments