LANCommander/LANCommander.SDK/GameManager.cs

240 lines
8.3 KiB
C#
Raw Normal View History

using LANCommander.SDK.Enums;
using LANCommander.SDK.Extensions;
using LANCommander.SDK.Helpers;
using LANCommander.SDK.Models;
using Microsoft.Extensions.Logging;
using SharpCompress.Common;
using SharpCompress.Readers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace LANCommander.SDK
{
public class GameManager
{
2023-11-11 02:53:48 +00:00
private readonly ILogger Logger;
private Client Client { get; set; }
private string DefaultInstallDirectory { get; set; }
public delegate void OnArchiveEntryExtractionProgressHandler(object sender, ArchiveEntryExtractionProgressArgs e);
public event OnArchiveEntryExtractionProgressHandler OnArchiveEntryExtractionProgress;
public delegate void OnArchiveExtractionProgressHandler(long position, long length);
public event OnArchiveExtractionProgressHandler OnArchiveExtractionProgress;
private TrackableStream Stream;
private IReader Reader;
public GameManager(Client client, string defaultInstallDirectory)
{
Client = client;
DefaultInstallDirectory = defaultInstallDirectory;
}
public GameManager(Client client, string defaultInstallDirectory, ILogger logger)
2023-11-11 02:53:48 +00:00
{
Client = client;
DefaultInstallDirectory = DefaultInstallDirectory;
2023-11-11 02:53:48 +00:00
Logger = logger;
}
/// <summary>
/// Downloads, extracts, and runs post-install scripts for the specified game
/// </summary>
/// <param name="game">Game to install</param>
/// <param name="maxAttempts">Maximum attempts in case of transmission error</param>
/// <returns>Final install path</returns>
/// <exception cref="Exception"></exception>
public string Install(Guid gameId, int maxAttempts = 10)
{
var game = Client.GetGame(gameId);
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Installing game {GameTitle} (GameId)", game.Title, game.Id);
var result = RetryHelper.RetryOnException<ExtractionResult>(maxAttempts, TimeSpan.FromMilliseconds(500), new ExtractionResult(), () =>
{
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Attempting to download and extract game");
return DownloadAndExtract(game, DefaultInstallDirectory);
});
if (!result.Success && !result.Canceled)
throw new Exception("Could not extract the installer. Retry the install or check your connection");
else if (result.Canceled)
return "";
GameManifest manifest = null;
game.InstallDirectory = result.Directory;
var writeManifestSuccess = RetryHelper.RetryOnException(maxAttempts, TimeSpan.FromSeconds(1), false, () =>
{
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Attempting to get game manifest");
manifest = Client.GetGameManifest(game.Id);
ManifestHelper.Write(manifest, game.InstallDirectory);
return true;
});
if (!writeManifestSuccess)
throw new Exception("Could not grab the manifest file. Retry the install or check your connection");
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Saving scripts");
ScriptHelper.SaveScript(game, ScriptType.Install);
ScriptHelper.SaveScript(game, ScriptType.Uninstall);
ScriptHelper.SaveScript(game, ScriptType.NameChange);
ScriptHelper.SaveScript(game, ScriptType.KeyChange);
try
{
PowerShellRuntime.RunScript(game, ScriptType.Install);
PowerShellRuntime.RunScript(game, ScriptType.NameChange, /* Plugin.Settings.PlayerName */ "");
var key = Client.GetAllocatedKey(game.Id);
PowerShellRuntime.RunScript(game, ScriptType.KeyChange, $"\"{key}\"");
}
catch (Exception ex)
{
2023-11-11 02:53:28 +00:00
Logger?.LogError(ex, "Could not execute post-install scripts");
}
return result.Directory;
}
public void Uninstall(string installDirectory)
{
var manifest = ManifestHelper.Read(installDirectory);
try
{
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Running uninstall script");
PowerShellRuntime.RunScript(installDirectory, ScriptType.Uninstall);
}
catch (Exception ex)
{
2023-11-11 02:53:28 +00:00
Logger?.LogError(ex, "Error running uninstall script");
}
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Attempting to delete the install directory");
if (Directory.Exists(installDirectory))
Directory.Delete(installDirectory, true);
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Deleted install directory {InstallDirectory}", installDirectory);
}
private ExtractionResult DownloadAndExtract(Game game, string installDirectory = "")
{
if (game == null)
{
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Game failed to download, no game was specified");
throw new ArgumentNullException("No game was specified");
}
if (String.IsNullOrWhiteSpace(installDirectory))
installDirectory = DefaultInstallDirectory;
var destination = Path.Combine(installDirectory, game.Title.SanitizeFilename());
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Downloading and extracting {Game} to path {Destination}", game.Title, destination);
var extractionResult = new ExtractionResult
{
Canceled = false,
};
try
{
Directory.CreateDirectory(destination);
Stream = Client.StreamGame(game.Id);
Reader = ReaderFactory.Open(Stream);
Stream.OnProgress += (pos, len) =>
{
OnArchiveExtractionProgress?.Invoke(pos, len);
};
Reader.EntryExtractionProgress += (object sender, ReaderExtractionEventArgs<IEntry> e) =>
{
OnArchiveEntryExtractionProgress?.Invoke(this, new ArchiveEntryExtractionProgressArgs
{
Entry = e.Item,
Progress = e.ReaderProgress,
});
};
while (Reader.MoveToNextEntry())
{
if (Reader.Cancelled)
break;
Reader.WriteEntryToDirectory(destination, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true,
PreserveFileTime = true,
});
}
Reader.Dispose();
Stream.Dispose();
}
catch (Exception ex)
{
if (Reader.Cancelled)
{
Logger?.LogTrace("User cancelled the download");
extractionResult.Canceled = true;
if (Directory.Exists(destination))
{
Logger?.LogTrace("Cleaning up orphaned files after cancelled install");
Directory.Delete(destination, true);
}
}
else
{
2023-11-11 02:53:28 +00:00
Logger?.LogError(ex, "Could not extract to path {Destination}", destination);
if (Directory.Exists(destination))
{
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Cleaning up orphaned install files after bad install");
Directory.Delete(destination, true);
}
throw new Exception("The game archive could not be extracted, is it corrupted? Please try again");
}
}
if (!extractionResult.Canceled)
{
extractionResult.Success = true;
extractionResult.Directory = destination;
2023-11-11 02:53:28 +00:00
Logger?.LogTrace("Game {Game} successfully downloaded and extracted to {Destination}", game.Title, destination);
}
return extractionResult;
}
public void CancelInstall()
{
Reader?.Cancel();
// Reader?.Dispose();
// Stream?.Dispose();
}
}
}