LANCommander/LANCommander.Playnite.Exten.../InstallController.cs

318 lines
11 KiB
C#
Raw Normal View History

2023-04-08 00:09:00 +00:00
using LANCommander.PlaynitePlugin.Helpers;
using LANCommander.SDK.Enums;
2023-01-07 04:12:03 +00:00
using LANCommander.SDK.Extensions;
2023-04-08 00:09:00 +00:00
using LANCommander.SDK.Models;
using Playnite.SDK;
using Playnite.SDK.Models;
using Playnite.SDK.Plugins;
using SharpCompress.Common;
using SharpCompress.Readers;
2023-01-07 04:12:03 +00:00
using System;
using System.IO;
using System.Linq;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
2023-01-07 04:12:03 +00:00
namespace LANCommander.PlaynitePlugin
2023-01-07 04:12:03 +00:00
{
public class LANCommanderInstallController : InstallController
{
2023-08-21 23:44:20 +00:00
public static readonly ILogger Logger = LogManager.GetLogger();
private LANCommanderLibraryPlugin Plugin;
2023-01-15 07:10:36 +00:00
private PowerShellRuntime PowerShellRuntime;
private Playnite.SDK.Models.Game PlayniteGame;
2023-01-07 04:12:03 +00:00
public LANCommanderInstallController(LANCommanderLibraryPlugin plugin, Playnite.SDK.Models.Game game) : base(game)
2023-01-07 04:12:03 +00:00
{
Name = "Install using LANCommander";
Plugin = plugin;
2023-01-15 07:10:36 +00:00
PlayniteGame = game;
PowerShellRuntime = new PowerShellRuntime();
2023-01-07 04:12:03 +00:00
}
public override void Install(InstallActionArgs args)
{
2023-08-21 23:44:20 +00:00
Logger.Trace("Game install triggered, checking connection...");
while (!Plugin.ValidateConnection())
{
2023-08-21 23:44:20 +00:00
Logger.Trace("User not authenticated. Opening auth window...");
Plugin.ShowAuthenticationWindow();
}
2023-01-07 04:12:03 +00:00
var gameId = Guid.Parse(Game.GameId);
var game = Plugin.LANCommander.GetGame(gameId);
2023-08-21 23:44:20 +00:00
Logger.Trace($"Installing game {game.Title} ({game.Id})...");
var result = RetryHelper.RetryOnException<ExtractionResult>(10, TimeSpan.FromMilliseconds(500), new ExtractionResult(), () =>
{
2023-08-21 23:44:20 +00:00
Logger.Trace("Attempting to download and extract game...");
2023-04-08 01:08:03 +00:00
return DownloadAndExtract(game);
});
if (!result.Success && !result.Canceled)
throw new Exception("Could not extract the install archive. Retry the install or check your connection.");
else if (result.Canceled)
throw new Exception("Install was canceled");
2023-01-15 07:10:36 +00:00
var installInfo = new GameInstallationData()
{
InstallDirectory = result.Directory
};
PlayniteGame.InstallDirectory = result.Directory;
2023-01-15 07:10:36 +00:00
SDK.GameManifest manifest = null;
var writeManifestSuccess = RetryHelper.RetryOnException(10, TimeSpan.FromSeconds(1), false, () =>
{
2023-08-21 23:44:20 +00:00
Logger.Trace("Attempting to get game manifest...");
manifest = Plugin.LANCommander.GetGameManifest(gameId);
WriteManifest(manifest, result.Directory);
return true;
});
if (!writeManifestSuccess)
throw new Exception("Could not get or write the manifest file. Retry the install or check your connection.");
2023-08-21 23:44:20 +00:00
Logger.Trace("Saving scripts...");
SaveScript(game, result.Directory, ScriptType.Install);
SaveScript(game, result.Directory, ScriptType.Uninstall);
SaveScript(game, result.Directory, ScriptType.NameChange);
SaveScript(game, result.Directory, ScriptType.KeyChange);
try
{
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.Install);
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.NameChange, Plugin.Settings.PlayerName);
2023-01-28 23:18:41 +00:00
var key = Plugin.LANCommander.GetAllocatedKey(game.Id);
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.KeyChange, $"\"{key}\"");
}
catch { }
2023-01-15 07:10:36 +00:00
Plugin.UpdateGame(manifest, gameId);
Plugin.DownloadCache.Remove(gameId);
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
2023-01-07 04:12:03 +00:00
}
private ExtractionResult DownloadAndExtract(LANCommander.SDK.Models.Game game)
2023-04-08 01:08:03 +00:00
{
if (game == null)
{
2023-08-21 23:44:20 +00:00
Logger.Trace("Game failed to download! No game was specified!");
2023-04-08 01:08:03 +00:00
throw new Exception("Game failed to download!");
}
var destination = Path.Combine(Plugin.Settings.InstallDirectory, game.Title.SanitizeFilename());
2023-08-22 23:58:59 +00:00
Logger.Trace($"Downloading and extracting \"{game.Title}\" to path {destination}");
var result = Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
2023-04-08 01:08:03 +00:00
{
2023-04-08 20:47:01 +00:00
try
2023-04-08 01:08:03 +00:00
{
2023-04-08 20:47:01 +00:00
Directory.CreateDirectory(destination);
progress.ProgressMaxValue = 100;
progress.CurrentProgressValue = 0;
2023-04-08 01:08:03 +00:00
2023-04-08 20:47:01 +00:00
using (var gameStream = Plugin.LANCommander.StreamGame(game.Id))
using (var reader = ReaderFactory.Open(gameStream))
2023-04-08 01:08:03 +00:00
{
2023-04-08 20:47:01 +00:00
progress.ProgressMaxValue = gameStream.Length;
gameStream.OnProgress += (pos, len) =>
{
progress.CurrentProgressValue = pos;
};
reader.EntryExtractionProgress += (object sender, ReaderExtractionEventArgs<IEntry> e) =>
{
if (progress.CancelToken != null && progress.CancelToken.IsCancellationRequested)
{
reader.Cancel();
progress.IsIndeterminate = true;
reader.Dispose();
gameStream.Dispose();
}
};
2023-04-08 20:47:01 +00:00
reader.WriteAllToDirectory(destination, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
catch (Exception ex)
{
if (progress.CancelToken != null && progress.CancelToken.IsCancellationRequested)
2023-04-08 01:08:03 +00:00
{
Logger.Trace("User cancelled the download");
2023-08-21 23:44:20 +00:00
if (Directory.Exists(destination))
{
Logger.Trace("Cleaning up orphaned install files after cancelled install...");
Directory.Delete(destination, true);
}
2023-04-08 20:47:01 +00:00
}
else
{
Logger.Error(ex, $"Could not extract to path {destination}");
if (Directory.Exists(destination))
{
Logger.Trace("Cleaning up orphaned install files after bad install...");
Directory.Delete(destination, true);
}
throw new Exception("The game archive could not be extracted. Please try again or fix the archive!");
}
2023-04-08 01:08:03 +00:00
}
},
new GlobalProgressOptions($"Downloading {game.Title}...")
{
IsIndeterminate = false,
Cancelable = true,
2023-04-08 01:08:03 +00:00
});
var extractionResult = new ExtractionResult
{
Canceled = result.Canceled
};
if (!result.Canceled)
{
extractionResult.Success = true;
extractionResult.Directory = destination;
Logger.Trace($"Game successfully downloaded and extracted to {destination}");
}
2023-08-21 23:44:20 +00:00
return extractionResult;
2023-04-08 01:08:03 +00:00
}
2023-01-07 04:12:03 +00:00
private string Download(LANCommander.SDK.Models.Game game)
{
string tempFile = String.Empty;
if (game != null)
2023-01-07 04:12:03 +00:00
{
Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
2023-01-07 04:12:03 +00:00
{
progress.ProgressMaxValue = 100;
progress.CurrentProgressValue = 0;
var destination = Plugin.LANCommander.DownloadGame(game.Id, (changed) =>
2023-01-07 04:12:03 +00:00
{
progress.CurrentProgressValue = changed.ProgressPercentage;
}, (complete) =>
{
progress.CurrentProgressValue = 100;
});
// Lock the thread until download is done
2023-01-07 04:12:03 +00:00
while (progress.CurrentProgressValue != 100)
{
}
tempFile = destination;
},
new GlobalProgressOptions($"Downloading {game.Title}...")
2023-01-07 04:12:03 +00:00
{
IsIndeterminate = false,
Cancelable = false,
});
return tempFile;
}
else
throw new Exception("Game failed to download!");
2023-01-07 04:12:03 +00:00
}
private string Extract(LANCommander.SDK.Models.Game game, string archivePath)
2023-01-07 04:12:03 +00:00
{
var destination = Path.Combine(Plugin.Settings.InstallDirectory, game.Title.SanitizeFilename());
2023-01-07 04:12:03 +00:00
Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
2023-01-07 04:12:03 +00:00
{
2023-04-08 00:09:00 +00:00
Directory.CreateDirectory(destination);
2023-01-07 04:12:03 +00:00
2023-04-08 00:09:00 +00:00
using (var fs = File.OpenRead(archivePath))
using (var ts = new TrackableStream(fs))
using (var reader = ReaderFactory.Open(ts))
2023-01-07 04:12:03 +00:00
{
2023-04-08 00:09:00 +00:00
progress.ProgressMaxValue = ts.Length;
ts.OnProgress += (pos, len) =>
2023-01-07 04:12:03 +00:00
{
2023-04-08 00:09:00 +00:00
progress.CurrentProgressValue = pos;
};
2023-04-08 00:09:00 +00:00
reader.WriteAllToDirectory(destination, new ExtractionOptions()
{
2023-04-08 00:09:00 +00:00
ExtractFullPath = true,
Overwrite = true
});
2023-01-07 04:12:03 +00:00
}
},
new GlobalProgressOptions($"Extracting {game.Title}...")
{
IsIndeterminate = false,
Cancelable = false,
});
2023-01-07 04:12:03 +00:00
return destination;
2023-01-07 04:12:03 +00:00
}
private void WriteManifest(SDK.GameManifest manifest, string installDirectory)
{
2023-08-21 23:44:20 +00:00
var destination = Path.Combine(installDirectory, "_manifest.yml");
Logger.Trace($"Attempting to write manifest to path {destination}");
var serializer = new SerializerBuilder()
.WithNamingConvention(new PascalCaseNamingConvention())
.Build();
2023-08-21 23:44:20 +00:00
Logger.Trace("Serializing manifest...");
var yaml = serializer.Serialize(manifest);
2023-08-21 23:44:20 +00:00
Logger.Trace("Writing manifest file...");
File.WriteAllText(destination, yaml);
}
private void SaveScript(LANCommander.SDK.Models.Game game, string installationDirectory, ScriptType type)
{
var script = game.Scripts.FirstOrDefault(s => s.Type == type);
if (script == null)
return;
if (script.RequiresAdmin)
script.Contents = "# Requires Admin" + "\r\n\r\n" + script.Contents;
var filename = PowerShellRuntime.GetScriptFilePath(PlayniteGame, type);
if (File.Exists(filename))
File.Delete(filename);
2023-08-21 23:44:20 +00:00
Logger.Trace($"Writing {type} script to {filename}");
File.WriteAllText(filename, script.Contents);
}
2023-01-07 04:12:03 +00:00
}
}