public async Task GameRunLoop() { var stopwatch = Stopwatch.StartNew(); do { if (hubConnection.State != HubConnectionState.Connected) { break; } if (!GameStarted) { if (!PendingStart) { Logger.LogInfo("RunLoop", "Waiting for all bots to connect"); } Thread.Sleep(1000); continue; } if (stopwatch.ElapsedMilliseconds < engineConfig.TickRate) { var delay = (int)(engineConfig.TickRate - stopwatch.ElapsedMilliseconds); if (delay > 0) { Thread.Sleep(delay); } } Logger.LogInfo("RunLoop", $"Game Loop Time: {stopwatch.ElapsedMilliseconds} milliseconds"); stopwatch.Restart(); Logger.LogDebug("Engine.ConnectionState", hubConnection.State); var elapsedPreTick = stopwatch.ElapsedMilliseconds; await ProcessGameTick(); Logger.LogDebug("RunLoop", $"Processing tick took {stopwatch.ElapsedMilliseconds - elapsedPreTick}ms"); await hubConnection.InvokeAsync("PublishGameState", worldStateService.GetPublishedState()); var elapsedTime = stopwatch.ElapsedMilliseconds; while (TickAcked != worldStateService.GetState().World.CurrentTick) { continue; } Logger.LogDebug("RunLoop", $"Waited {stopwatch.ElapsedMilliseconds - elapsedTime}ms for TickAck"); } while (!HasWinner && hubConnection.State == HubConnectionState.Connected); if (!HasWinner && hubConnection.State != HubConnectionState.Connected) { Logger.LogError("GameRunLoop", "Runner disconnected before a winner was found"); throw new InvalidOperationException("Runner disconnected before a winner was found"); } await hubConnection.InvokeAsync("GameComplete", worldStateService.GenerateGameCompletePayload()); }