v2.3.3.0
- Improved SteamCMD cleanup process - Improved SteamCMD appinfo caching, upping the minimum appinfo version to facilitate changes - Implemented SteamCMD process limit of 20, resulting in less CPU usage with faster and more stable appinfo gathering
This commit is contained in:
parent
3d780e55d2
commit
e1e9b1ccdc
4 changed files with 114 additions and 61 deletions
|
@ -6,6 +6,7 @@ using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
@ -17,59 +18,60 @@ namespace CreamInstaller.Classes;
|
||||||
|
|
||||||
internal static class SteamCMD
|
internal static class SteamCMD
|
||||||
{
|
{
|
||||||
|
internal static readonly int ProcessLimit = 20;
|
||||||
|
internal static readonly Version MinimumAppInfoVersion = Version.Parse("2.3.3.0");
|
||||||
|
|
||||||
internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
|
internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller";
|
||||||
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
|
internal static readonly string FilePath = DirectoryPath + @"\steamcmd.exe";
|
||||||
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
|
|
||||||
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
|
|
||||||
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
|
|
||||||
internal static readonly string AppCacheAppInfoPath = AppCachePath + @"\appinfo.vdf";
|
|
||||||
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
|
|
||||||
|
|
||||||
internal static readonly Version MinimumAppInfoVersion = Version.Parse("2.0.3.2");
|
private static readonly int[] locks = new int[ProcessLimit];
|
||||||
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
|
|
||||||
|
|
||||||
//private static readonly int[] locks = new int[20]; // acts as an effective process limit
|
|
||||||
internal static async Task<string> Run(string command) => await Task.Run(() =>
|
internal static async Task<string> Run(string command) => await Task.Run(() =>
|
||||||
{
|
{
|
||||||
/*wait_for_lock:
|
wait_for_lock:
|
||||||
|
if (Program.Canceled) return "";
|
||||||
|
for (int i = 0; i < locks.Length; i++)
|
||||||
|
{
|
||||||
if (Program.Canceled) return "";
|
if (Program.Canceled) return "";
|
||||||
for (int i = 0; i < locks.Length; i++)
|
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0)
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return "";
|
if (Program.Canceled) return "";
|
||||||
if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0)
|
List<string> logs = new();
|
||||||
{*/
|
ProcessStartInfo processStartInfo = new()
|
||||||
if (Program.Canceled) return "";
|
{
|
||||||
List<string> logs = new();
|
FileName = FilePath,
|
||||||
ProcessStartInfo processStartInfo = new()
|
RedirectStandardOutput = true,
|
||||||
{
|
RedirectStandardInput = true,
|
||||||
FileName = FilePath,
|
RedirectStandardError = true,
|
||||||
RedirectStandardOutput = true,
|
UseShellExecute = false,
|
||||||
RedirectStandardInput = true,
|
Arguments = command,
|
||||||
RedirectStandardError = true,
|
CreateNoWindow = true,
|
||||||
UseShellExecute = false,
|
StandardInputEncoding = Encoding.UTF8,
|
||||||
Arguments = command,
|
StandardOutputEncoding = Encoding.UTF8,
|
||||||
CreateNoWindow = true,
|
StandardErrorEncoding = Encoding.UTF8
|
||||||
StandardInputEncoding = Encoding.UTF8,
|
};
|
||||||
StandardOutputEncoding = Encoding.UTF8,
|
using Process process = Process.Start(processStartInfo);
|
||||||
StandardErrorEncoding = Encoding.UTF8
|
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
||||||
};
|
process.BeginOutputReadLine();
|
||||||
using Process process = Process.Start(processStartInfo);
|
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
||||||
process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
process.BeginErrorReadLine();
|
||||||
process.BeginOutputReadLine();
|
process.WaitForExit();
|
||||||
process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => logs.Add(e.Data);
|
Interlocked.Decrement(ref locks[i]);
|
||||||
process.BeginErrorReadLine();
|
return string.Join("\r\n", logs);
|
||||||
process.WaitForExit();
|
}
|
||||||
//Interlocked.Decrement(ref locks[i]);
|
Thread.Sleep(0);
|
||||||
return string.Join("\r\n", logs);
|
}
|
||||||
/*}
|
|
||||||
Thread.Sleep(200);
|
Thread.Sleep(200);
|
||||||
}
|
goto wait_for_lock;
|
||||||
goto wait_for_lock;*/
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
internal static readonly string ArchivePath = DirectoryPath + @"\steamcmd.zip";
|
||||||
|
internal static readonly string DllPath = DirectoryPath + @"\steamclient.dll";
|
||||||
|
internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo";
|
||||||
|
internal static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt";
|
||||||
|
|
||||||
internal static async Task Setup(IProgress<int> progress = null)
|
internal static async Task Setup(IProgress<int> progress = null)
|
||||||
{
|
{
|
||||||
await Kill();
|
await Cleanup();
|
||||||
if (!Directory.Exists(DirectoryPath)) Directory.CreateDirectory(DirectoryPath);
|
if (!Directory.Exists(DirectoryPath)) Directory.CreateDirectory(DirectoryPath);
|
||||||
if (!File.Exists(FilePath))
|
if (!File.Exists(FilePath))
|
||||||
{
|
{
|
||||||
|
@ -81,7 +83,6 @@ internal static class SteamCMD
|
||||||
ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath);
|
ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath);
|
||||||
File.Delete(ArchivePath);
|
File.Delete(ArchivePath);
|
||||||
}
|
}
|
||||||
if (File.Exists(AppCacheAppInfoPath)) File.Delete(AppCacheAppInfoPath);
|
|
||||||
if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) || version < MinimumAppInfoVersion)
|
if (!File.Exists(AppInfoVersionPath) || !Version.TryParse(File.ReadAllText(AppInfoVersionPath, Encoding.UTF8), out Version version) || version < MinimumAppInfoVersion)
|
||||||
{
|
{
|
||||||
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true);
|
if (Directory.Exists(AppInfoPath)) Directory.Delete(AppInfoPath, true);
|
||||||
|
@ -100,38 +101,82 @@ internal static class SteamCMD
|
||||||
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($@"+quit");
|
||||||
await Cleanup();
|
|
||||||
watcher.Dispose();
|
watcher.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task Cleanup() => await Task.Run(() =>
|
internal static readonly string AppCachePath = DirectoryPath + @"\appcache";
|
||||||
|
internal static readonly string ConfigPath = DirectoryPath + @"\config";
|
||||||
|
internal static readonly string DumpsPath = DirectoryPath + @"\dumps";
|
||||||
|
internal static readonly string LogsPath = DirectoryPath + @"\logs";
|
||||||
|
internal static readonly string SteamAppsPath = DirectoryPath + @"\steamapps";
|
||||||
|
internal static readonly string UserDataPath = DirectoryPath + @"\userdata";
|
||||||
|
|
||||||
|
internal static async Task Cleanup() => await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
|
await Kill();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string[] oldFiles = Directory.GetFiles(DirectoryPath, "*.old");
|
string[] oldFiles = Directory.GetFiles(DirectoryPath, "*.old");
|
||||||
foreach (string file in oldFiles) File.Delete(file);
|
foreach (string file in oldFiles) File.Delete(file);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
string[] deleteFiles = Directory.GetFiles(DirectoryPath, "*.delete");
|
string[] deleteFiles = Directory.GetFiles(DirectoryPath, "*.delete");
|
||||||
foreach (string file in deleteFiles) File.Delete(file);
|
foreach (string file in deleteFiles) File.Delete(file);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string[] crashFiles = Directory.GetFiles(DirectoryPath, "*.crash");
|
||||||
|
foreach (string file in crashFiles) File.Delete(file);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(AppCachePath)) Directory.Delete(AppCachePath, true);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(ConfigPath)) Directory.Delete(ConfigPath, true);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(DumpsPath)) Directory.Delete(DumpsPath, true);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(LogsPath)) Directory.Delete(LogsPath, true);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(SteamAppsPath)) Directory.Delete(SteamAppsPath, true);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(UserDataPath)) Directory.Delete(UserDataPath, true);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
});
|
});
|
||||||
|
|
||||||
internal static async Task<VProperty> GetAppInfo(int appId, string branch = "public", int buildId = 0)
|
internal static async Task<VProperty> GetAppInfo(int appId, string branch = "public", int buildId = 0)
|
||||||
{
|
{
|
||||||
if (Program.Canceled) return null;
|
if (Program.Canceled) return null;
|
||||||
string output;
|
string output;
|
||||||
string appUpdatePath = $@"{AppInfoPath}\{appId}";
|
string appUpdateFile = $@"{AppInfoPath}\{appId}.vdf";
|
||||||
string appUpdateFile = $@"{appUpdatePath}\appinfo.txt";
|
|
||||||
restart:
|
restart:
|
||||||
await Cleanup();
|
|
||||||
if (Program.Canceled) return null;
|
if (Program.Canceled) return null;
|
||||||
if (!Directory.Exists(appUpdatePath)) Directory.CreateDirectory(appUpdatePath);
|
|
||||||
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
|
// we add app_update 4 to allow the app_info_print to finish
|
||||||
output = await Run($@"@ShutdownOnFailedCommand 0 +force_install_dir {appUpdatePath} +login anonymous +app_info_print {appId} +app_update 4 +quit");
|
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)
|
||||||
|
@ -143,13 +188,13 @@ internal static class SteamCMD
|
||||||
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))
|
||||||
{
|
{
|
||||||
Directory.Delete(appUpdatePath, true);
|
File.Delete(appUpdateFile);
|
||||||
//new DialogForm(null).Show("AppInfoAttempts", SystemIcons.Information, "Deserialize exception:\n\n" + output, "OK");
|
//new DialogForm(null).Show("GetAppInfo", SystemIcons.Information, "Deserialize exception:\n\n" + output, "OK");
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
if (appInfo.Value is VValue)
|
if (appInfo.Value is VValue)
|
||||||
{
|
{
|
||||||
//new DialogForm(null).Show("AppInfoAttempts", SystemIcons.Information, "VValue exception:\n\n" + output, "OK");
|
//new DialogForm(null).Show("GetAppInfo", SystemIcons.Information, "VValue exception:\n\n" + output, "OK");
|
||||||
goto restart;
|
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;
|
||||||
|
@ -163,10 +208,10 @@ internal static class SteamCMD
|
||||||
List<int> dlcAppIds = await ParseDlcAppIds(appInfo);
|
List<int> dlcAppIds = await ParseDlcAppIds(appInfo);
|
||||||
foreach (int id in dlcAppIds)
|
foreach (int id in dlcAppIds)
|
||||||
{
|
{
|
||||||
string dlcAppUpdatePath = $@"{AppInfoPath}\{id}";
|
string dlcAppUpdateFile = $@"{AppInfoPath}\{id}.vdf";
|
||||||
if (Directory.Exists(dlcAppUpdatePath)) Directory.Delete(dlcAppUpdatePath, true);
|
if (File.Exists(dlcAppUpdateFile)) File.Delete(dlcAppUpdateFile);
|
||||||
}
|
}
|
||||||
if (Directory.Exists(appUpdatePath)) Directory.Delete(appUpdatePath, true);
|
if (File.Exists(appUpdateFile)) File.Delete(appUpdateFile);
|
||||||
goto restart;
|
goto restart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,12 +244,15 @@ internal static class SteamCMD
|
||||||
List<Task> tasks = new();
|
List<Task> tasks = new();
|
||||||
foreach (Process process in Process.GetProcessesByName("steamcmd"))
|
foreach (Process process in Process.GetProcessesByName("steamcmd"))
|
||||||
{
|
{
|
||||||
try
|
tasks.Add(Task.Run(() =>
|
||||||
{
|
{
|
||||||
process.Kill();
|
try
|
||||||
tasks.Add(Task.Run(() => process.WaitForExit()));
|
{
|
||||||
}
|
process.Kill(true);
|
||||||
catch { }
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
foreach (Task task in tasks) await task;
|
foreach (Task task in tasks) await task;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>2.3.2.0</Version>
|
<Version>2.3.3.0</Version>
|
||||||
<PackageIcon>Resources\ini.ico</PackageIcon>
|
<PackageIcon>Resources\ini.ico</PackageIcon>
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<Description>Automatically generates and installs CreamAPI files for Steam games on the user's computer. It can also generate and install CreamAPI for the Paradox Launcher should the user select a Paradox Interactive game.</Description>
|
<Description>Automatically generates and installs CreamAPI files for Steam games on the user's computer. It can also generate and install CreamAPI for the Paradox Launcher should the user select a Paradox Interactive game.</Description>
|
||||||
|
|
|
@ -379,7 +379,12 @@ internal partial class SelectForm : CustomForm
|
||||||
{
|
{
|
||||||
if (!int.TryParse(node.Name, out int appId) || node.Parent is null && ProgramSelection.FromAppId(appId) is null) node.Remove();
|
if (!int.TryParse(node.Name, out int appId) || node.Parent is null && ProgramSelection.FromAppId(appId) is null) node.Remove();
|
||||||
});
|
});
|
||||||
|
//DateTime start = DateTime.Now;
|
||||||
await GetCreamApiApplicablePrograms(iProgress);
|
await GetCreamApiApplicablePrograms(iProgress);
|
||||||
|
//DateTime end = DateTime.Now;
|
||||||
|
//TimeSpan t = end - start;
|
||||||
|
//new DialogForm(null).Show("GetCreamApiApplicablePrograms", SystemIcons.Information, "Gathering took " + t.ToString(@"mm\:ss"), "OK");
|
||||||
|
await SteamCMD.Cleanup();
|
||||||
|
|
||||||
HideProgressBar();
|
HideProgressBar();
|
||||||
selectionTreeView.Enabled = ProgramSelection.All.Any();
|
selectionTreeView.Enabled = ProgramSelection.All.Any();
|
||||||
|
|
|
@ -122,7 +122,7 @@ internal static class Program
|
||||||
internal static async void Cleanup(bool cancel = true)
|
internal static async void Cleanup(bool cancel = true)
|
||||||
{
|
{
|
||||||
Canceled = cancel;
|
Canceled = cancel;
|
||||||
await SteamCMD.Kill();
|
await SteamCMD.Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Invoke(this Control control, MethodInvoker methodInvoker) => control.Invoke(methodInvoker);
|
internal static void Invoke(this Control control, MethodInvoker methodInvoker) => control.Invoke(methodInvoker);
|
||||||
|
|
Loading…
Reference in a new issue