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>
|
<UseWindowsForms>True</UseWindowsForms>
|
||||||
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
||||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||||
<Version>3.0.2.1</Version>
|
<Version>3.1.0.0</Version>
|
||||||
<PackageIcon>Resources\ini.ico</PackageIcon>
|
<PackageIcon>Resources\ini.ico</PackageIcon>
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<Description />
|
<Description />
|
||||||
|
|
|
@ -81,12 +81,21 @@ internal partial class SelectForm : CustomForm
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly List<Task> RunningTasks = new();
|
|
||||||
private async Task GetApplicablePrograms(IProgress<int> progress)
|
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;
|
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
|
RemainingGames.Clear(); // for display purposes only, otherwise ignorable
|
||||||
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable
|
RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable
|
||||||
List<Task> appTasks = new();
|
List<Task> appTasks = new();
|
||||||
|
@ -169,11 +178,8 @@ internal partial class SelectForm : CustomForm
|
||||||
if (!string.IsNullOrWhiteSpace(dlcName))
|
if (!string.IsNullOrWhiteSpace(dlcName))
|
||||||
dlc[dlcAppId] = (DlcType.Default, dlcName, dlcIconStaticId);
|
dlc[dlcAppId] = (DlcType.Default, dlcName, dlcIconStaticId);
|
||||||
RemoveFromRemainingDLCs(dlcAppId);
|
RemoveFromRemainingDLCs(dlcAppId);
|
||||||
progress.Report(++CompleteTasks);
|
|
||||||
});
|
});
|
||||||
dlcTasks.Add(task);
|
dlcTasks.Add(task);
|
||||||
RunningTasks.Add(task);
|
|
||||||
progress.Report(-RunningTasks.Count);
|
|
||||||
Thread.Sleep(10); // to reduce control & window freezing
|
Thread.Sleep(10); // to reduce control & window freezing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,11 +236,8 @@ internal partial class SelectForm : CustomForm
|
||||||
});
|
});
|
||||||
if (Program.Canceled) return;
|
if (Program.Canceled) return;
|
||||||
RemoveFromRemainingGames(name);
|
RemoveFromRemainingGames(name);
|
||||||
progress.Report(++CompleteTasks);
|
|
||||||
});
|
});
|
||||||
appTasks.Add(task);
|
appTasks.Add(task);
|
||||||
RunningTasks.Add(task);
|
|
||||||
progress.Report(-RunningTasks.Count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Directory.Exists(EpicLibrary.EpicAppDataPath))
|
if (Directory.Exists(EpicLibrary.EpicAppDataPath))
|
||||||
|
@ -273,11 +276,8 @@ internal partial class SelectForm : CustomForm
|
||||||
if (Program.Canceled) return;
|
if (Program.Canceled) return;
|
||||||
entitlements[id] = (name, product, icon, developer);
|
entitlements[id] = (name, product, icon, developer);
|
||||||
RemoveFromRemainingDLCs(id);
|
RemoveFromRemainingDLCs(id);
|
||||||
progress.Report(++CompleteTasks);
|
|
||||||
});
|
});
|
||||||
dlcTasks.Add(task);
|
dlcTasks.Add(task);
|
||||||
RunningTasks.Add(task);
|
|
||||||
progress.Report(-RunningTasks.Count);
|
|
||||||
Thread.Sleep(10); // to reduce control & window freezing
|
Thread.Sleep(10); // to reduce control & window freezing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,11 +350,8 @@ internal partial class SelectForm : CustomForm
|
||||||
});
|
});
|
||||||
if (Program.Canceled) return;
|
if (Program.Canceled) return;
|
||||||
RemoveFromRemainingGames(name);
|
RemoveFromRemainingGames(name);
|
||||||
progress.Report(++CompleteTasks);
|
|
||||||
});
|
});
|
||||||
appTasks.Add(task);
|
appTasks.Add(task);
|
||||||
RunningTasks.Add(task);
|
|
||||||
progress.Report(-RunningTasks.Count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (Task task in appTasks)
|
foreach (Task task in appTasks)
|
||||||
|
@ -362,7 +359,6 @@ internal partial class SelectForm : CustomForm
|
||||||
if (Program.Canceled) return;
|
if (Program.Canceled) return;
|
||||||
await task;
|
await task;
|
||||||
}
|
}
|
||||||
progress.Report(RunningTasks.Count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnLoad()
|
private async void OnLoad()
|
||||||
|
|
|
@ -18,15 +18,18 @@ namespace CreamInstaller.Steam;
|
||||||
|
|
||||||
internal static class SteamCMD
|
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 DirectoryPath => ProgramData.DirectoryPath;
|
||||||
internal static string AppInfoPath => ProgramData.AppInfoPath;
|
internal static string AppInfoPath => ProgramData.AppInfoPath;
|
||||||
|
|
||||||
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
|
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];
|
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:
|
wait_for_lock:
|
||||||
if (Program.Canceled) return "";
|
if (Program.Canceled) return "";
|
||||||
|
@ -35,6 +38,13 @@ internal static class SteamCMD
|
||||||
if (Program.Canceled) return "";
|
if (Program.Canceled) return "";
|
||||||
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0)
|
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 "";
|
if (Program.Canceled) return "";
|
||||||
List<string> logs = new();
|
List<string> logs = new();
|
||||||
ProcessStartInfo processStartInfo = new()
|
ProcessStartInfo processStartInfo = new()
|
||||||
|
@ -44,20 +54,55 @@ internal static class SteamCMD
|
||||||
RedirectStandardInput = true,
|
RedirectStandardInput = true,
|
||||||
RedirectStandardError = true,
|
RedirectStandardError = true,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
Arguments = command,
|
Arguments = appId is null ? "+quit" : GetArguments(appId),
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
StandardInputEncoding = Encoding.UTF8,
|
StandardInputEncoding = Encoding.UTF8,
|
||||||
StandardOutputEncoding = Encoding.UTF8,
|
StandardOutputEncoding = Encoding.UTF8,
|
||||||
StandardErrorEncoding = Encoding.UTF8
|
StandardErrorEncoding = Encoding.UTF8
|
||||||
};
|
};
|
||||||
using Process process = Process.Start(processStartInfo);
|
Process process = Process.Start(processStartInfo);
|
||||||
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
string output = "";
|
||||||
process.BeginOutputReadLine();
|
string appInfo = "";
|
||||||
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
bool appInfoStarted = false;
|
||||||
process.BeginErrorReadLine();
|
DateTime lastOutput = DateTime.UtcNow;
|
||||||
process.WaitForExit();
|
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]);
|
Interlocked.Decrement(ref locks[i]);
|
||||||
return string.Join("\r\n", logs);
|
return appInfo;
|
||||||
}
|
}
|
||||||
Thread.Sleep(0);
|
Thread.Sleep(0);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +137,7 @@ internal static class SteamCMD
|
||||||
int cur = 0;
|
int cur = 0;
|
||||||
progress.Report(cur);
|
progress.Report(cur);
|
||||||
watcher.Changed += (sender, e) => progress.Report(++cur);
|
watcher.Changed += (sender, e) => progress.Report(++cur);
|
||||||
await Run($@"+quit");
|
await Run(null);
|
||||||
watcher.Dispose();
|
watcher.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,8 +213,7 @@ internal static class SteamCMD
|
||||||
if (File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
|
if (File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile, Encoding.UTF8);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// we add app_update 4 to allow the app_info_print to finish
|
output = await Run(appId);
|
||||||
output = await Run($@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} +app_update 4 +quit");
|
|
||||||
int openBracket = output.IndexOf("{");
|
int openBracket = output.IndexOf("{");
|
||||||
int closeBracket = output.LastIndexOf("}");
|
int closeBracket = output.LastIndexOf("}");
|
||||||
if (openBracket != -1 && closeBracket != -1)
|
if (openBracket != -1 && closeBracket != -1)
|
||||||
|
@ -178,17 +222,14 @@ internal static class SteamCMD
|
||||||
output = output.Replace("ERROR! Failed to install app '4' (Invalid platform)", "");
|
output = output.Replace("ERROR! Failed to install app '4' (Invalid platform)", "");
|
||||||
File.WriteAllText(appUpdateFile, output, Encoding.UTF8);
|
File.WriteAllText(appUpdateFile, output, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
|
else goto restart;
|
||||||
}
|
}
|
||||||
if (Program.Canceled || output is null) return null;
|
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);
|
File.Delete(appUpdateFile);
|
||||||
//new DialogForm(null).Show("GetAppInfo", SystemIcons.Information, "Deserialize exception:\n\n" + output, "OK");
|
|
||||||
goto restart;
|
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;
|
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo;
|
||||||
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type");
|
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type");
|
||||||
if (type is null || type.ToString() == "Game")
|
if (type is null || type.ToString() == "Game")
|
||||||
|
@ -243,6 +284,7 @@ internal static class SteamCMD
|
||||||
{
|
{
|
||||||
process.Kill(true);
|
process.Kill(true);
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
|
process.Close();
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in a new issue