private static void DownloadManifest(ManifestJob request) { DepotManifest depotManifest = null; string lastError = string.Empty; // CDN is very random, just keep trying for (var i = 0; i <= 5; i++) { try { depotManifest = CDNClient.DownloadManifest(request.DepotID, request.ManifestID, request.Server, request.DepotKey, request.CDNToken); break; } catch (Exception e) { lastError = e.Message; } } if (depotManifest == null) { Log.WriteError("Depot Processor", "Failed to download depot manifest for depot {0} (jobs still in queue: {1}) ({2}: {3})", request.DepotID, ManifestJobs.Count - 1, request.Server, lastError); if (SteamProxy.Instance.ImportantApps.Contains(request.ParentAppID)) { IRC.SendMain("Important depot update: {0}{1}{2} -{3} failed to download depot manifest", Colors.OLIVE, request.DepotName, Colors.NORMAL, Colors.RED); } return; } if (SteamProxy.Instance.ImportantApps.Contains(request.ParentAppID)) { IRC.SendMain("Important depot update: {0}{1}{2} -{3} {4}", Colors.OLIVE, request.DepotName, Colors.NORMAL, Colors.DARK_BLUE, SteamDB.GetDepotURL(request.DepotID, "history")); } var sortedFiles = depotManifest.Files.OrderBy(f => f.FileName, StringComparer.OrdinalIgnoreCase); bool shouldHistorize = false; var filesNew = new List <DepotFile>(); var filesOld = new Dictionary <string, DepotFile>(); foreach (var file in sortedFiles) { System.Text.Encoding.UTF8.GetString(file.FileHash); var depotFile = new DepotFile { Name = file.FileName.Replace('\\', '/'), Size = file.TotalSize, Chunks = file.Chunks.Count, Flags = (int)file.Flags }; // TODO: Ideally we would check if filehash is not empty if (!file.Flags.HasFlag(EDepotFileFlag.Directory)) { depotFile.Hash = string.Concat(Array.ConvertAll(file.FileHash, x => x.ToString("X2"))); } filesNew.Add(depotFile); } using (MySqlDataReader Reader = DbWorker.ExecuteReader("SELECT `Files` FROM `Depots` WHERE `DepotID` = @DepotID LIMIT 1", new MySqlParameter("DepotID", request.DepotID))) { if (Reader.Read()) { string files = Reader.GetString("Files"); if (!string.IsNullOrEmpty(files)) { shouldHistorize = true; var _filesOld = JsonConvert.DeserializeObject <List <DepotFile> >(files); filesOld = _filesOld.ToDictionary(x => x.Name); } } } DbWorker.ExecuteNonQuery("UPDATE `Depots` SET `Files` = @Files WHERE `DepotID` = @DepotID", new MySqlParameter("@DepotID", request.DepotID), new MySqlParameter("@Files", JsonConvert.SerializeObject(filesNew, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore })) ); if (shouldHistorize) { var filesAdded = new List <string>(); foreach (var file in filesNew) { if (filesOld.ContainsKey(file.Name)) { var oldFile = filesOld[file.Name]; if (oldFile.Size != file.Size) { MakeHistory(request, file.Name, "modified", oldFile.Size, file.Size); } else if (file.Hash != null && oldFile.Hash != null && !file.Hash.Equals(oldFile.Hash)) { MakeHistory(request, file.Name, "modified", oldFile.Size, file.Size); } filesOld.Remove(file.Name); } else { // We want to historize modifications first, and only then deletions and additions filesAdded.Add(file.Name); } } foreach (var file in filesOld) { MakeHistory(request, file.Value.Name, "removed"); } foreach (string file in filesAdded) { MakeHistory(request, file, "added"); } } lock (ManifestJobs) { Log.WriteDebug("Depot Processor", "DepotID: Processed {0} (jobs still in queue: {1})", request.DepotID, ManifestJobs.Count - 1); } }