v3.1.0.0
- Overhauled SteamCMD appinfo gathering, with substantially increased speed - Increased maximum SteamCMD processes from 20 to 30 - The gathering progress is now based on only games, so no more progress decreasing
This commit is contained in:
parent
be28b0db3e
commit
7cae752787
3 changed files with 73 additions and 35 deletions
|
@ -5,7 +5,7 @@
|
|||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||
<Version>3.0.2.1</Version>
|
||||
<Version>3.1.0.0</Version>
|
||||
<PackageIcon>Resources\ini.ico</PackageIcon>
|
||||
<PackageIconUrl />
|
||||
<Description />
|
||||
|
|
|
@ -81,12 +81,21 @@ internal partial class SelectForm : CustomForm
|
|||
});
|
||||
}
|
||||
|
||||
internal readonly List<Task> RunningTasks = new();
|
||||
private async Task GetApplicablePrograms(IProgress<int> progress)
|
||||
{
|
||||
int TotalGameCount = 0;
|
||||
int CompleteGameCount = 0;
|
||||
void AddToRemainingGames(string gameName)
|
||||
{
|
||||
this.AddToRemainingGames(gameName);
|
||||
progress.Report(-++TotalGameCount);
|
||||
}
|
||||
void RemoveFromRemainingGames(string gameName)
|
||||
{
|
||||
this.RemoveFromRemainingGames(gameName);
|
||||
progress.Report(++CompleteGameCount);
|
||||
}
|
||||
if (Program.Canceled) return;
|
||||
int CompleteTasks = 0;
|
||||
RunningTasks.Clear(); // contains all running tasks including games AND their dlc
|
||||
RemainingGames.Clear(); // for display purposes only, otherwise ignorable
|
||||
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable
|
||||
List<Task> appTasks = new();
|
||||
|
@ -169,11 +178,8 @@ internal partial class SelectForm : CustomForm
|
|||
if (!string.IsNullOrWhiteSpace(dlcName))
|
||||
dlc[dlcAppId] = (DlcType.Default, dlcName, dlcIconStaticId);
|
||||
RemoveFromRemainingDLCs(dlcAppId);
|
||||
progress.Report(++CompleteTasks);
|
||||
});
|
||||
dlcTasks.Add(task);
|
||||
RunningTasks.Add(task);
|
||||
progress.Report(-RunningTasks.Count);
|
||||
Thread.Sleep(10); // to reduce control & window freezing
|
||||
}
|
||||
}
|
||||
|
@ -230,11 +236,8 @@ internal partial class SelectForm : CustomForm
|
|||
});
|
||||
if (Program.Canceled) return;
|
||||
RemoveFromRemainingGames(name);
|
||||
progress.Report(++CompleteTasks);
|
||||
});
|
||||
appTasks.Add(task);
|
||||
RunningTasks.Add(task);
|
||||
progress.Report(-RunningTasks.Count);
|
||||
}
|
||||
}
|
||||
if (Directory.Exists(EpicLibrary.EpicAppDataPath))
|
||||
|
@ -273,11 +276,8 @@ internal partial class SelectForm : CustomForm
|
|||
if (Program.Canceled) return;
|
||||
entitlements[id] = (name, product, icon, developer);
|
||||
RemoveFromRemainingDLCs(id);
|
||||
progress.Report(++CompleteTasks);
|
||||
});
|
||||
dlcTasks.Add(task);
|
||||
RunningTasks.Add(task);
|
||||
progress.Report(-RunningTasks.Count);
|
||||
Thread.Sleep(10); // to reduce control & window freezing
|
||||
}
|
||||
}
|
||||
|
@ -350,11 +350,8 @@ internal partial class SelectForm : CustomForm
|
|||
});
|
||||
if (Program.Canceled) return;
|
||||
RemoveFromRemainingGames(name);
|
||||
progress.Report(++CompleteTasks);
|
||||
});
|
||||
appTasks.Add(task);
|
||||
RunningTasks.Add(task);
|
||||
progress.Report(-RunningTasks.Count);
|
||||
}
|
||||
}
|
||||
foreach (Task task in appTasks)
|
||||
|
@ -362,7 +359,6 @@ internal partial class SelectForm : CustomForm
|
|||
if (Program.Canceled) return;
|
||||
await task;
|
||||
}
|
||||
progress.Report(RunningTasks.Count);
|
||||
}
|
||||
|
||||
private async void OnLoad()
|
||||
|
|
|
@ -18,15 +18,18 @@ namespace CreamInstaller.Steam;
|
|||
|
||||
internal static class SteamCMD
|
||||
{
|
||||
internal static readonly int ProcessLimit = 20;
|
||||
internal static readonly int ProcessLimit = 30;
|
||||
|
||||
internal static string DirectoryPath => ProgramData.DirectoryPath;
|
||||
internal static string AppInfoPath => ProgramData.AppInfoPath;
|
||||
|
||||
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
|
||||
|
||||
private static readonly Dictionary<string, int> AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print
|
||||
private static string GetArguments(string appId) => $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} " + string.Concat(Enumerable.Repeat("+app_update 4 ", AttemptCount[appId])) + "+quit";
|
||||
|
||||
private static readonly int[] locks = new int[ProcessLimit];
|
||||
internal static async Task<string> Run(string command) => await Task.Run(() =>
|
||||
internal static async Task<string> Run(string appId) => await Task.Run(() =>
|
||||
{
|
||||
wait_for_lock:
|
||||
if (Program.Canceled) return "";
|
||||
|
@ -35,6 +38,13 @@ internal static class SteamCMD
|
|||
if (Program.Canceled) return "";
|
||||
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0)
|
||||
{
|
||||
if (appId is not null)
|
||||
{
|
||||
if (AttemptCount.ContainsKey(appId))
|
||||
AttemptCount[appId]++;
|
||||
else
|
||||
AttemptCount[appId] = 0;
|
||||
}
|
||||
if (Program.Canceled) return "";
|
||||
List<string> logs = new();
|
||||
ProcessStartInfo processStartInfo = new()
|
||||
|
@ -44,20 +54,55 @@ internal static class SteamCMD
|
|||
RedirectStandardInput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
Arguments = command,
|
||||
Arguments = appId is null ? "+quit" : GetArguments(appId),
|
||||
CreateNoWindow = true,
|
||||
StandardInputEncoding = Encoding.UTF8,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
};
|
||||
using Process process = Process.Start(processStartInfo);
|
||||
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
||||
process.BeginOutputReadLine();
|
||||
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
||||
process.BeginErrorReadLine();
|
||||
process.WaitForExit();
|
||||
Process process = Process.Start(processStartInfo);
|
||||
string output = "";
|
||||
string appInfo = "";
|
||||
bool appInfoStarted = false;
|
||||
DateTime lastOutput = DateTime.UtcNow;
|
||||
while (true)
|
||||
{
|
||||
if (Program.Canceled)
|
||||
{
|
||||
process.Kill(true);
|
||||
process.Close();
|
||||
break;
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
int c = process.StandardOutput.Read();
|
||||
if (c != -1)
|
||||
{
|
||||
lastOutput = DateTime.UtcNow;
|
||||
char ch = (char)c;
|
||||
if (ch == '{') appInfoStarted = true;
|
||||
if (appInfoStarted) appInfo += ch;
|
||||
else output += ch;
|
||||
}
|
||||
DateTime now = DateTime.UtcNow;
|
||||
TimeSpan timeDiff = now - lastOutput;
|
||||
if (timeDiff.TotalSeconds > 0.1)
|
||||
{
|
||||
process.Kill(true);
|
||||
process.Close();
|
||||
if (output.Contains($"No app info for AppID {appId} found, requesting..."))
|
||||
{
|
||||
AttemptCount[appId]++;
|
||||
processStartInfo.Arguments = GetArguments(appId);
|
||||
process = Process.Start(processStartInfo);
|
||||
appInfoStarted = false;
|
||||
output = "";
|
||||
appInfo = "";
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
Interlocked.Decrement(ref locks[i]);
|
||||
return string.Join("\r\n", logs);
|
||||
return appInfo;
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
}
|
||||
|
@ -92,7 +137,7 @@ internal static class SteamCMD
|
|||
int cur = 0;
|
||||
progress.Report(cur);
|
||||
watcher.Changed += (sender, e) => progress.Report(++cur);
|
||||
await Run($@"+quit");
|
||||
await Run(null);
|
||||
watcher.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -168,8 +213,7 @@ internal static class SteamCMD
|
|||
if (File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
|
||||
else
|
||||
{
|
||||
// we add app_update 4 to allow the app_info_print to finish
|
||||
output = await Run($@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} +app_update 4 +quit");
|
||||
output = await Run(appId);
|
||||
int openBracket = output.IndexOf("{");
|
||||
int closeBracket = output.LastIndexOf("}");
|
||||
if (openBracket != -1 && closeBracket != -1)
|
||||
|
@ -178,17 +222,14 @@ internal static class SteamCMD
|
|||
output = output.Replace("ERROR! Failed to install app '4' (Invalid platform)", "");
|
||||
File.WriteAllText(appUpdateFile, output, Encoding.UTF8);
|
||||
}
|
||||
else goto restart;
|
||||
}
|
||||
if (Program.Canceled || output is null) return null;
|
||||
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo))
|
||||
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue)
|
||||
{
|
||||
File.Delete(appUpdateFile);
|
||||
//new DialogForm(null).Show("GetAppInfo", SystemIcons.Information, "Deserialize exception:\n\n" + output, "OK");
|
||||
goto restart;
|
||||
}
|
||||
if (appInfo.Value is VValue)
|
||||
//new DialogForm(null).Show("GetAppInfo", SystemIcons.Information, "VValue exception:\n\n" + output, "OK");
|
||||
goto restart;
|
||||
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo;
|
||||
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type");
|
||||
if (type is null || type.ToString() == "Game")
|
||||
|
@ -243,6 +284,7 @@ internal static class SteamCMD
|
|||
{
|
||||
process.Kill(true);
|
||||
process.WaitForExit();
|
||||
process.Close();
|
||||
}
|
||||
catch { }
|
||||
}));
|
||||
|
|
Loading…
Reference in a new issue