From eaef1160c330ea60c33346b4cf6f1e7ec30532cf Mon Sep 17 00:00:00 2001 From: Ryan Kistner Date: Tue, 18 Nov 2014 14:29:14 -0700 Subject: [PATCH] Added retry logic to steam requests and made downloader "retry forever" where appropriate. --- DepotDownloader/ContentDownloader.cs | 48 ++++++++++----- DepotDownloader/Steam3Session.cs | 91 ++++++++++++---------------- 2 files changed, 73 insertions(+), 66 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 69fc3fca..2146d273 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -466,9 +466,8 @@ namespace DepotDownloader var cdnClients = new BlockingCollection(); CDNClient initialClient = new CDNClient( steam3.steamClient, steam3.AppTickets[depot.id] ); List cdnServers = null; - int tries = 5; - while ( tries-- > 0 ) + while(true) { try { @@ -477,7 +476,8 @@ namespace DepotDownloader } catch (WebException) { - Console.WriteLine("\nFailed to retrieve content server list. Remaining tries: {0}", tries); + Console.WriteLine("\nFailed to retrieve content server list."); + Thread.Sleep(500); } } @@ -553,8 +553,7 @@ namespace DepotDownloader Console.WriteLine("Downloading depot {0} - {1}", depot.id, depot.contentName); var cdnClientsLock = new Object(); - BlockingCollection cdnClients = null; - int liveCdnClients = 0; + BlockingCollection cdnClients = null; CancellationTokenSource cts = new CancellationTokenSource(); ProtoManifest oldProtoManifest = null; @@ -599,7 +598,6 @@ namespace DepotDownloader DepotManifest depotManifest = null; cdnClients = CollectCDNClientsForDepot(depot); - liveCdnClients = cdnClients.Count; foreach (var c in cdnClients) { @@ -793,7 +791,6 @@ namespace DepotDownloader if (cdnClients == null) { cdnClients = CollectCDNClientsForDepot(depot); - liveCdnClients = cdnClients.Count; } } } @@ -805,7 +802,7 @@ namespace DepotDownloader string chunkID = Util.EncodeHexString(chunk.ChunkID); CDNClient.DepotChunk chunkData = null; - while (liveCdnClients > 0 && !cts.IsCancellationRequested) + while (!cts.IsCancellationRequested) { CDNClient client; try @@ -827,18 +824,39 @@ namespace DepotDownloader try { chunkData = client.DownloadDepotChunk(depot.id, data); - cdnClients.Add(client); break; } - catch (Exception e) + catch (WebException e) { - Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, liveCdnClients); - int liveCount = Interlocked.Decrement(ref liveCdnClients); - if (liveCount == 0) + if (e.Status == WebExceptionStatus.ProtocolError) + { + var response = e.Response as HttpWebResponse; + if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) + { + Console.WriteLine("Encountered 401 for chunk {0}. Aborting."); + cts.Cancel(); + break; + } + else + { + Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, response.StatusCode); + } + } + else { - // we've run out of clients, tell other threads to abort - cts.Cancel(); + Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, e.Status); } + + // let client "cool off" before re-adding it to the queue + Thread.Sleep(500); + } + catch (Exception e) + { + Console.WriteLine("Encountered unexpected error downloading chunk {0}: {1}", chunkID, e.Message); + } + finally + { + cdnClients.Add(client); } } diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index c766e954..360fc1b8 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -46,6 +46,7 @@ namespace DepotDownloader bool bConnected; bool bConnecting; bool bAborted; + int seq; // more hack fixes DateTime connectTime; // input @@ -66,6 +67,7 @@ namespace DepotDownloader this.bConnected = false; this.bConnecting = false; this.bAborted = false; + this.seq = 0; this.AppTickets = new Dictionary(); this.AppTokens = new Dictionary(); @@ -109,16 +111,30 @@ namespace DepotDownloader Connect(); } + public delegate bool WaitCondition(); + public bool WaitUntilCallback(Action submitter, WaitCondition waiter) + { + while (!bAborted && !waiter()) + { + submitter(); + + int seq = this.seq; + do + { + WaitForCallbacks(); + } + while (!bAborted && this.seq == seq && !waiter()); + } + + return bAborted; + } + public Credentials WaitForCredentials() { if (credentials.IsValid || bAborted) return credentials; - do - { - WaitForCallbacks(); - } - while (!bAborted && !credentials.IsValid); + WaitUntilCallback(() => { }, () => { return credentials.IsValid; }); return credentials; } @@ -143,14 +159,9 @@ namespace DepotDownloader } }; - using (var appTokensCallback = new Callback(cbMethodTokens, callbacks, steamApps.PICSGetAccessTokens(new List() { appId }, new List() { }))) - { - do - { - WaitForCallbacks(); - } - while (!completed && !bAborted); - } + WaitUntilCallback(() => { + new Callback(cbMethodTokens, callbacks, steamApps.PICSGetAccessTokens(new List() { appId }, new List() { })); + }, () => { return completed; }); completed = false; Action cbMethod = (appInfo) => @@ -178,14 +189,9 @@ namespace DepotDownloader request.Public = false; } - using (var appInfoCallback = new Callback(cbMethod, callbacks, steamApps.PICSGetProductInfo(new List() { request }, new List() { }))) - { - do - { - WaitForCallbacks(); - } - while (!completed && !bAborted); - } + WaitUntilCallback(() => { + new Callback(cbMethod, callbacks, steamApps.PICSGetProductInfo(new List() { request }, new List() { })); + }, () => { return completed; }); } public void RequestPackageInfo(IEnumerable packageIds) @@ -213,14 +219,9 @@ namespace DepotDownloader } }; - using (var packageInfoCallback = new Callback(cbMethod, callbacks, steamApps.PICSGetProductInfo(new List(), packages))) - { - do - { - WaitForCallbacks(); - } - while (!completed && !bAborted); - } + WaitUntilCallback(() => { + new Callback(cbMethod, callbacks, steamApps.PICSGetProductInfo(new List(), packages)); + }, () => { return completed; }); } public void RequestAppTicket(uint appId) @@ -252,14 +253,9 @@ namespace DepotDownloader } }; - using (var appTicketCallback = new Callback(cbMethod, callbacks, steamApps.GetAppOwnershipTicket(appId))) - { - do - { - WaitForCallbacks(); - } - while (!completed && !bAborted); - } + WaitUntilCallback(() => { + new Callback(cbMethod, callbacks, steamApps.GetAppOwnershipTicket(appId)); + }, () => { return completed; }); } public void RequestDepotKey(uint depotId, uint appid = 0) @@ -283,14 +279,10 @@ namespace DepotDownloader DepotKeys[depotKey.DepotID] = depotKey.DepotKey; }; - using ( var depotKeyCallback = new Callback( cbMethod, callbacks, steamApps.GetDepotDecryptionKey( depotId, appid ) ) ) + WaitUntilCallback(() => { - do - { - WaitForCallbacks(); - } - while ( !completed && !bAborted ); - } + new Callback(cbMethod, callbacks, steamApps.GetDepotDecryptionKey(depotId, appid)); + }, () => { return completed; }); } public void RequestCDNAuthToken(uint depotid, string host) @@ -313,14 +305,10 @@ namespace DepotDownloader CDNAuthTokens[Tuple.Create(depotid, host)] = cdnAuth; }; - using (var cdnAuthCallback = new Callback(cbMethod, callbacks, steamApps.GetCDNAuthToken(depotid, host))) + WaitUntilCallback(() => { - do - { - WaitForCallbacks(); - } - while (!completed && !bAborted); - } + new Callback(cbMethod, callbacks, steamApps.GetCDNAuthToken(depotid, host)); + }, () => { return completed; }); } void Connect() @@ -424,6 +412,7 @@ namespace DepotDownloader Console.WriteLine(" Done!"); + this.seq++; credentials.LoggedOn = true; if (ContentDownloader.Config.CellID == 0)