void OnProductInfo(SteamApps.PICSProductInfoCallback callback) { if (callback.ResponsePending) { return; } var req = GetRequest(r => r.JobID == callback.JobID); if (req == null) { return; } bool isUnknownPackage = callback.UnknownPackages.Contains(req.PackageID) || !callback.Packages.ContainsKey(req.PackageID); if (isUnknownPackage) { IRC.Instance.Send(req.Channel, "{0}: Unable to request package info for {1}: unknown PackageID", req.Requester.Nickname, req.PackageID); return; } var packageInfo = callback.Packages[req.PackageID]; var kv = packageInfo.KeyValues.Children .FirstOrDefault(); // sigh, inconsistencies var path = Path.Combine("packageinfo", string.Format("{0}.vdf", req.PackageID)); var fsPath = Path.Combine(Settings.Current.WebPath, path); var webUri = new Uri(new Uri(Settings.Current.WebURL), path); try { kv.SaveToFile(fsPath, false); } catch (IOException ex) { IRC.Instance.Send(req.Channel, "{0}: Unable to save package info for {1} to web path!", req.Requester.Nickname, req.PackageID); Log.WriteError("PackageInfoCommand", "Unable to save package info for {0} to web path: {1}", req.PackageID, ex); return; } IRC.Instance.Send(req.Channel, "{0}: {1} {2}", req.Requester.Nickname, webUri, packageInfo.MissingToken ? "(requires token)" : ""); }
public static async Task HandleMetadataInfo(SteamApps.PICSProductInfoCallback callback) { var apps = new List <uint>(); var subs = new List <uint>(); await using var db = await Database.GetConnectionAsync(); if (callback.Apps.Any()) { Log.WriteDebug(nameof(FullUpdateProcessor), $"Received metadata only product info for {callback.Apps.Count} apps ({callback.Apps.First().Key}...{callback.Apps.Last().Key}), job: {callback.JobID}"); var currentChangeNumbers = (await db.QueryAsync <(uint, uint)>( "SELECT `AppID`, `Value` FROM `AppsInfo` WHERE `Key` = @ChangeNumberKey AND `AppID` IN @Apps", new { ChangeNumberKey = KeyNameCache.GetAppKeyID("root_changenumber"), Apps = callback.Apps.Keys } )).ToDictionary(x => x.Item1, x => x.Item2); foreach (var app in callback.Apps.Values) { currentChangeNumbers.TryGetValue(app.ID, out var currentChangeNumber); if (currentChangeNumber == app.ChangeNumber) { continue; } Log.WriteInfo(nameof(FullUpdateProcessor), $"App {app.ID} - Change: {currentChangeNumber} -> {app.ChangeNumber}"); apps.Add(app.ID); if (!Settings.IsFullRun) { await db.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { app.ChangeNumber }); await db.ExecuteAsync("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID = app.ID, app.ChangeNumber }); } } } if (callback.Packages.Any()) { Log.WriteDebug(nameof(FullUpdateProcessor), $"Received metadata only product info for {callback.Packages.Count} packages ({callback.Packages.First().Key}...{callback.Packages.Last().Key}), job: {callback.JobID}"); var currentChangeNumbers = (await db.QueryAsync <(uint, uint)>( "SELECT `SubID`, `Value` FROM `SubsInfo` WHERE `Key` = @ChangeNumberKey AND `SubID` IN @Subs", new { ChangeNumberKey = KeyNameCache.GetSubKeyID("root_changenumber"), Subs = callback.Packages.Keys } )).ToDictionary(x => x.Item1, x => x.Item2); foreach (var sub in callback.Packages.Values) { currentChangeNumbers.TryGetValue(sub.ID, out var currentChangeNumber); if (currentChangeNumber == sub.ChangeNumber) { continue; } Log.WriteInfo(nameof(FullUpdateProcessor), $"Package {sub.ID} - Change: {currentChangeNumber} -> {sub.ChangeNumber}"); subs.Add(sub.ID); if (!Settings.IsFullRun) { await db.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { sub.ChangeNumber }); await db.ExecuteAsync("INSERT INTO `ChangelistsSubs` (`ChangeID`, `SubID`) VALUES (@ChangeNumber, @SubID) ON DUPLICATE KEY UPDATE `SubID` = `SubID`", new { SubID = sub.ID, sub.ChangeNumber }); } } } if (apps.Any() || subs.Any()) { JobManager.AddJob( () => Steam.Instance.Apps.PICSGetAccessTokens(apps, subs), new PICSTokens.RequestedTokens { Apps = apps, Packages = subs, }); } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobManager.TryRemoveJob(callback.JobID); var processors = new List <BaseProcessor>( callback.Apps.Count + callback.Packages.Count + callback.UnknownApps.Count + callback.UnknownPackages.Count ); processors.AddRange(callback.Apps.Select(app => new AppProcessor(app.Key, app.Value))); processors.AddRange(callback.Packages.Select(package => new SubProcessor(package.Key, package.Value))); processors.AddRange(callback.UnknownApps.Select(app => new AppProcessor(app, null))); processors.AddRange(callback.UnknownPackages.Select(package => new SubProcessor(package, null))); foreach (var workaround in processors) { var processor = workaround; Task mostRecentItem; lock (CurrentlyProcessing) { CurrentlyProcessing.TryGetValue(processor.Id, out mostRecentItem); } var workerItem = TaskManager.Run(async() => { try { await Semaphore.WaitAsync(TaskManager.TaskCancellationToken.Token).ConfigureAwait(false); if (mostRecentItem?.IsCompleted == false) { Log.WriteDebug(processor.ToString(), $"Waiting for previous task to finish processing ({CurrentlyProcessing.Count})"); await mostRecentItem.ConfigureAwait(false); #if DEBUG Log.WriteDebug(processor.ToString(), "Previous task lock ended"); #endif } await processor.Process().ConfigureAwait(false); } catch (Exception e) { ErrorReporter.Notify(processor.ToString(), e); } finally { Semaphore.Release(); processor.Dispose(); } return(processor); }).Unwrap(); lock (CurrentlyProcessing) { CurrentlyProcessing[processor.Id] = workerItem; } // Register error handler on inner task and the continuation TaskManager.RegisterErrorHandler(workerItem); TaskManager.RegisterErrorHandler(workerItem.ContinueWith(RemoveProcessorLock, TaskManager.TaskCancellationToken.Token)); } }
private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) { if (callback.Result != EResult.OK) { if (callback.Result == EResult.AccountLogonDenied) { // if we recieve AccountLogonDenied or one of it's flavors (AccountLogonDeniedNoMailSent, etc) // then the account we're logging into is SteamGuard protected // see sample 5 for how SteamGuard can be handled _logger.LogWarning("Unable to logon to Steam: This account is SteamGuard protected."); return; } _logger.LogError("Unable to logon to Steam: {0} / {1}", callback.Result, callback.ExtendedResult); return; } // in this sample, we'll simply do a few async requests to acquire information about appid 440 (Team Fortress 2) // first, we'll request a depot decryption key for TF2's client/server shared depot (441) var depotJob = steamApps.GetDepotDecryptionKey(depotid: 441, appid: 440); // at this point, this request is now in-flight to the steam server, so we'll use te async/await pattern to wait for a response // the await pattern allows this code to resume once the Steam servers have replied to the request. // if Steam does not reply to the request in a timely fashion (controlled by the `Timeout` field on the AsyncJob object), the underlying // task for this job will be cancelled, and TaskCanceledException will be thrown. // additionally, if Steam encounters a remote failure and is unable to process your request, the job will be faulted and an AsyncJobFailedException // will be thrown. SteamApps.DepotKeyCallback depotKey = await depotJob; if (depotKey.Result == EResult.OK) { _logger.LogDebug($"Got our depot key: {BitConverter.ToString(depotKey.DepotKey)}"); } else { _logger.LogDebug("Unable to request depot key!"); } // now request some product info for TF2 var productJob = steamApps.PICSGetProductInfo(440, package: null); // note that with some requests, Steam can return multiple results, so these jobs don't return the callback object directly, but rather // a result set that could contain multiple callback objects if Steam gives us multiple results AsyncJobMultiple <SteamApps.PICSProductInfoCallback> .ResultSet resultSet = await productJob; if (resultSet.Complete) { // the request fully completed, we can handle the data SteamApps.PICSProductInfoCallback productInfo = resultSet.Results.First(); // ... do something with our product info } else if (resultSet.Failed) { // the request partially completed, and then Steam encountered a remote failure. for async jobs with only a single result (such as // GetDepotDecryptionKey), this would normally throw an AsyncJobFailedException. but since Steam had given us a partial set of callbacks // we get to decide what to do with the data // keep in mind that if Steam immediately fails to provide any data, or times out while waiting for the first result, an // AsyncJobFailedException or TaskCanceledException will be thrown // the result set might not have our data, so we need to test to see if we have results for our request SteamApps.PICSProductInfoCallback productInfo = resultSet.Results.FirstOrDefault(prodCallback => prodCallback.Apps.ContainsKey(440)); if (productInfo != null) { // we were lucky and Steam gave us the info we requested before failing } else { // bad luck } } else { // the request partially completed, but then we timed out. essentially the same as the previous case, but Steam didn't explicitly fail. // we still need to check our result set to see if we have our data SteamApps.PICSProductInfoCallback productInfo = resultSet.Results.FirstOrDefault(prodCallback => prodCallback.Apps.ContainsKey(440)); if (productInfo != null) { // we were lucky and Steam gave us the info we requested before timing out } else { // bad luck } } // lastly, if you're unable to use the async/await pattern (older VS/compiler, etc) you can still directly access the TPL Task associated // with the async job by calling `ToTask()` var depotTask = steamApps.GetDepotDecryptionKey(depotid: 441, appid: 440).ToTask(); // set up a continuation for when this task completes var ignored = depotTask.ContinueWith(task => { depotKey = task.Result; // do something with the depot key // we're finished with this sample, drop out of the callback loop }, TaskContinuationOptions.OnlyOnRanToCompletion); }
public void OnProductInfo(IRCRequest request, SteamApps.PICSProductInfoCallback callback) { if (request.Type == SteamProxy.IRCRequestType.TYPE_SUB) { if (!callback.Packages.ContainsKey(request.Target)) { CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Unknown SubID: {3}{4}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, Colors.OLIVE, request.Target); return; } var info = callback.Packages[request.Target]; var kv = info.KeyValues.Children.FirstOrDefault(); // Blame VoiDeD string name = string.Format("SubID {0}", info.ID); if (kv["name"].Value != null) { name = kv["name"].AsString(); } try { kv.SaveToFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "sub", string.Format("{0}.vdf", info.ID)), false); } catch (Exception e) { CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Unable to save file for {3}: {4}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, name, e.Message); return; } CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Dump for {3}{4}{5} -{6} {7}{8}{9}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, Colors.OLIVE, name, Colors.NORMAL, Colors.DARK_BLUE, SteamDB.GetRawPackageURL(info.ID), Colors.NORMAL, info.MissingToken ? " (missing token)" : string.Empty ); } else if (request.Type == SteamProxy.IRCRequestType.TYPE_APP) { if (!callback.Apps.ContainsKey(request.Target)) { CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Unknown AppID: {3}{4}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, Colors.OLIVE, request.Target); return; } var info = callback.Apps[request.Target]; string name = string.Format("AppID {0}", info.ID); if (info.KeyValues["common"]["name"].Value != null) { name = info.KeyValues["common"]["name"].AsString(); } try { info.KeyValues.SaveToFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "app", string.Format("{0}.vdf", info.ID)), false); } catch (Exception e) { CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Unable to save file for {3}: {4}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, name, e.Message); return; } CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Dump for {3}{4}{5} -{6} {7}{8}{9}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, Colors.OLIVE, name, Colors.NORMAL, Colors.DARK_BLUE, SteamDB.GetRawAppURL(info.ID), Colors.NORMAL, info.MissingToken ? " (missing token)" : string.Empty ); } else { CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: I have no idea what happened here!", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL); } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { var apps = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); foreach (var workaround in apps) { var app = workaround; Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key); Task mostRecentItem; lock (ProcessedApps) { ProcessedApps.TryGetValue(app.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key); await mostRecentItem; } using (var processor = new AppProcessor(app.Key)) { if (app.Value == null) { processor.ProcessUnknown(); } else { processor.Process(app.Value); } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedApps) { ProcessedApps[app.Key] = workerItem; } workerItem.ContinueWith(task => { lock (ProcessedApps) { if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedApps.Remove(app.Key); } } }); } foreach (var workaround in packages) { var package = workaround; Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key); Task mostRecentItem; lock (ProcessedSubs) { ProcessedSubs.TryGetValue(package.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key); await mostRecentItem; } using (var processor = new SubProcessor(package.Key)) { if (package.Value == null) { processor.ProcessUnknown(); } else { processor.Process(package.Value); } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedSubs) { ProcessedSubs[package.Key] = workerItem; } workerItem.ContinueWith(task => { lock (ProcessedSubs) { if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedSubs.Remove(package.Key); } } }); } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobManager.TryRemoveJob(callback.JobID); var processors = new List <BaseProcessor>( callback.Apps.Count + callback.Packages.Count + callback.UnknownApps.Count + callback.UnknownPackages.Count ); processors.AddRange(callback.Apps.Select(app => new AppProcessor(app.Key, app.Value))); processors.AddRange(callback.Packages.Select(package => new SubProcessor(package.Key, package.Value))); processors.AddRange(callback.UnknownApps.Select(app => new AppProcessor(app, null))); processors.AddRange(callback.UnknownPackages.Select(package => new SubProcessor(package, null))); foreach (var workaround in processors) { var processor = workaround; Task mostRecentItem; lock (CurrentlyProcessing) { CurrentlyProcessing.TryGetValue(processor.Id, out mostRecentItem); } var workerItem = TaskManager.Run(async() => { await Semaphore.WaitAsync(TaskManager.TaskCancellationToken.Token).ConfigureAwait(false); try { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for {0} to finish processing", processor.ToString()); await mostRecentItem.ConfigureAwait(false); } await processor.Process().ConfigureAwait(false); } finally { Semaphore.Release(); processor.Dispose(); } }).Unwrap(); lock (CurrentlyProcessing) { CurrentlyProcessing[processor.Id] = workerItem; } workerItem.ContinueWith(task => { lock (CurrentlyProcessing) { if (CurrentlyProcessing.TryGetValue(processor.Id, out mostRecentItem) && mostRecentItem.IsCompleted) { CurrentlyProcessing.Remove(processor.Id); } } }, TaskManager.TaskCancellationToken.Token); } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobManager.TryRemoveJob(callback.JobID); var apps = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); foreach (var workaround in apps) { var app = workaround; Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key); IWorkItemResult mostRecentItem; lock (ProcessedApps) { ProcessedApps.TryGetValue(app.Key, out mostRecentItem); } var workerItem = ProcessorThreadPool.QueueWorkItem(delegate { try { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } using (var processor = new AppProcessor(app.Key)) { if (app.Value == null) { processor.ProcessUnknown(); } else { processor.Process(app.Value); } } } catch (MySqlException e) { Log.WriteError("PICSProductInfo", "App {0} faulted: {1}", app.Key, e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(app.Key, null)); } catch (Exception e) { Log.WriteError("PICSProductInfo", "App {0} faulted: {1}", app.Key, e); } finally { lock (ProcessedApps) { if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedApps.Remove(app.Key); } } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedApps) { ProcessedApps[app.Key] = workerItem; } } foreach (var workaround in packages) { var package = workaround; Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key); IWorkItemResult mostRecentItem; lock (ProcessedSubs) { ProcessedSubs.TryGetValue(package.Key, out mostRecentItem); } var workerItem = ProcessorThreadPool.QueueWorkItem(delegate { try { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } using (var processor = new SubProcessor(package.Key)) { if (package.Value == null) { processor.ProcessUnknown(); } else { processor.Process(package.Value); } } } catch (MySqlException e) { Log.WriteError("PICSProductInfo", "Package {0} faulted: {1}", package.Key, e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(null, package.Key, false, false)); } catch (Exception e) { Log.WriteError("PICSProductInfo", "Package {0} faulted: {1}", package.Key, e); } finally { lock (ProcessedSubs) { if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedSubs.Remove(package.Key); } } } }); if (Settings.IsFullRun) { continue; } lock (ProcessedSubs) { ProcessedSubs[package.Key] = workerItem; } } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobManager.TryRemoveJob(callback.JobID); var apps = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null)); foreach (var workaround in apps) { var app = workaround; Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key); Task mostRecentItem; lock (ProcessedApps) { ProcessedApps.TryGetValue(app.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async() => { try { await ProcessorSemaphore.WaitAsync().ConfigureAwait(false); if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key); await mostRecentItem.ConfigureAwait(false); } using (var processor = new AppProcessor(app.Key)) { if (app.Value == null) { processor.ProcessUnknown(); } else { processor.Process(app.Value); } } } catch (MySqlException e) { ErrorReporter.Notify($"App {app.Key}", e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(app.Key, null)); } catch (Exception e) { ErrorReporter.Notify($"App {app.Key}", e); } finally { lock (ProcessedApps) { if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedApps.Remove(app.Key); } } ProcessorSemaphore.Release(); } }); if (Settings.IsFullRun) { continue; } lock (ProcessedApps) { ProcessedApps[app.Key] = workerItem; } } foreach (var workaround in packages) { var package = workaround; Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key); Task mostRecentItem; lock (ProcessedSubs) { ProcessedSubs.TryGetValue(package.Key, out mostRecentItem); } var workerItem = TaskManager.Run(async() => { try { await ProcessorSemaphore.WaitAsync().ConfigureAwait(false); if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key); await mostRecentItem.ConfigureAwait(false); } using (var processor = new SubProcessor(package.Key)) { if (package.Value == null) { processor.ProcessUnknown(); } else { processor.Process(package.Value); } } } catch (MySqlException e) { ErrorReporter.Notify($"Package {package.Key}", e); JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(null, package.Key, false, false)); } catch (Exception e) { ErrorReporter.Notify($"Package {package.Key}", e); } finally { lock (ProcessedSubs) { if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted) { ProcessedSubs.Remove(package.Key); } } ProcessorSemaphore.Release(); } }); if (Settings.IsFullRun) { continue; } lock (ProcessedSubs) { ProcessedSubs[package.Key] = workerItem; } } }
private void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback, JobID jobID) { var request = SteamProxy.Instance.IRCRequests.Find(r => r.JobID == jobID); if (request != null) { SteamProxy.Instance.IRCRequests.Remove(request); SecondaryPool.QueueWorkItem(SteamProxy.Instance.OnProductInfo, request, callback); return; } foreach (var app in callback.Apps) { Log.WriteInfo("Steam", "AppID: {0}", app.Key); var workaround = app; IWorkItemResult mostRecentItem; ProcessedApps.TryGetValue(workaround.Key, out mostRecentItem); var workerItem = ProcessorPool.QueueWorkItem(delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("Steam", "Waiting for app {0} to finish processing", workaround.Key); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } new AppProcessor(workaround.Key).Process(workaround.Value); }); ProcessedApps.AddOrUpdate(app.Key, workerItem, (key, oldValue) => workerItem); } foreach (var package in callback.Packages) { Log.WriteInfo("Steam", "SubID: {0}", package.Key); var workaround = package; IWorkItemResult mostRecentItem; ProcessedSubs.TryGetValue(workaround.Key, out mostRecentItem); var workerItem = ProcessorPool.QueueWorkItem(delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("Steam", "Waiting for package {0} to finish processing", workaround.Key); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } new SubProcessor(workaround.Key).Process(workaround.Value); }); ProcessedSubs.AddOrUpdate(package.Key, workerItem, (key, oldValue) => workerItem); } foreach (uint app in callback.UnknownApps) { Log.WriteInfo("Steam", "Unknown AppID: {0}", app); uint workaround = app; IWorkItemResult mostRecentItem; ProcessedApps.TryGetValue(workaround, out mostRecentItem); var workerItem = ProcessorPool.QueueWorkItem(delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("Steam", "Waiting for app {0} to finish processing (unknown)", workaround); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } new AppProcessor(workaround).ProcessUnknown(); }); ProcessedApps.AddOrUpdate(app, workerItem, (key, oldValue) => workerItem); } foreach (uint package in callback.UnknownPackages) { Log.WriteInfo("Steam", "Unknown SubID: {0}", package); uint workaround = package; IWorkItemResult mostRecentItem; ProcessedSubs.TryGetValue(workaround, out mostRecentItem); var workerItem = ProcessorPool.QueueWorkItem(delegate { if (mostRecentItem != null && !mostRecentItem.IsCompleted) { Log.WriteDebug("Steam", "Waiting for package {0} to finish processing (unknown)", workaround); SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem }); } new SubProcessor(workaround).ProcessUnknown(); }); ProcessedSubs.AddOrUpdate(package, workerItem, (key, oldValue) => workerItem); } }
private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback) { JobAction job; if (!JobManager.TryRemoveJob(callback.JobID, out job) || !job.IsCommand) { return; } var request = job.CommandRequest; if (request.Type == JobManager.IRCRequestType.TYPE_SUB) { if (!callback.Packages.ContainsKey(request.Target)) { CommandHandler.ReplyToCommand(request.Command, "Unknown SubID: {0}{1}{2}", Colors.BLUE, request.Target, LicenseList.OwnedSubs.ContainsKey(request.Target) ? SteamDB.StringCheckmark : string.Empty); return; } var info = callback.Packages[request.Target]; var kv = info.KeyValues.Children.FirstOrDefault(); string name; if (kv["name"].Value != null) { name = Utils.RemoveControlCharacters(kv["name"].AsString()); } else { name = Steam.GetPackageName(info.ID); } try { kv.SaveToFile(Path.Combine(Application.Path, "sub", string.Format("{0}.vdf", info.ID)), false); } catch (Exception e) { CommandHandler.ReplyToCommand(request.Command, "Unable to save file for {0}: {1}", name, e.Message); return; } CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}", Colors.BLUE, name, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetPackageURL(info.ID), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetRawPackageURL(info.ID), Colors.NORMAL, info.MissingToken ? SteamDB.StringNeedToken : string.Empty, LicenseList.OwnedSubs.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty ); } else if (request.Type == JobManager.IRCRequestType.TYPE_APP) { if (!callback.Apps.ContainsKey(request.Target)) { CommandHandler.ReplyToCommand(request.Command, "Unknown AppID: {0}{1}{2}", Colors.BLUE, request.Target, LicenseList.OwnedApps.ContainsKey(request.Target) ? SteamDB.StringCheckmark : string.Empty); return; } var info = callback.Apps[request.Target]; string name; if (info.KeyValues["common"]["name"].Value != null) { name = Utils.RemoveControlCharacters(info.KeyValues["common"]["name"].AsString()); } else { name = Steam.GetAppName(info.ID); } try { info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", string.Format("{0}.vdf", info.ID)), false); } catch (Exception e) { CommandHandler.ReplyToCommand(request.Command, "Unable to save file for {0}: {1}", name, e.Message); return; } CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}", Colors.BLUE, name, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(info.ID), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetRawAppURL(info.ID), Colors.NORMAL, info.MissingToken ? SteamDB.StringNeedToken : string.Empty, LicenseList.OwnedApps.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty ); } else { CommandHandler.ReplyToCommand(request.Command, "I have no idea what happened here!"); } }