Files
minecraft-modpack-updater/ModpackUpdater.Manager/ModpackInstaller.cs

226 lines
8.4 KiB
C#

using ModpackUpdater.Model;
using System.IO.Compression;
namespace ModpackUpdater.Manager;
public class ModpackInstaller(ModpackConfig updateConfig, string localPath)
{
private class LocalZipCacheEntry
{
public string DownloadUrl { get; set; }
public string ExtractedZipPath { get; set; }
}
public event InstallProgessUpdatedEventHandler InstallProgessUpdated;
public delegate void InstallProgessUpdatedEventHandler(UpdateCheckResult result, int processedSyncs);
public event CheckingProgressUpdatedEventHandler CheckingProgressUpdated;
public delegate void CheckingProgressUpdatedEventHandler(int toCheck, int processed);
private readonly HttpClient httpClient = new();
~ModpackInstaller()
{
httpClient.Dispose();
}
private async Task<UpdateInfos> DownloadUpdateInfos()
{
var content = await httpClient.GetStringAsync(updateConfig.UpdateUrl);
return UpdateInfos.Parse(content);
}
private async Task<InstallInfos> DownloadInstallInfos()
{
var content = await httpClient.GetStringAsync(updateConfig.InstallUrl);
return InstallInfos.Parse(content);
}
public async Task<UpdateCheckResult> Check(bool allowUpdaterAfterInstall = true)
{
var result = new UpdateCheckResult();
var hasConfig = ModpackInfo.HasModpackInfo(localPath);
InstallInfos installInfos = null;
UpdateInfos updateInfos = null;
if (!hasConfig)
{
installInfos = await DownloadInstallInfos();
if (installInfos is not null && installInfos.Actions.Count != 0)
{
result.Actions.AddRange(installInfos.Actions);
result.LatestVersion = installInfos.Version;
}
else
result.HasError = true;
}
if (allowUpdaterAfterInstall)
{
updateInfos = await DownloadUpdateInfos();
var modpackInfo = ModpackInfo.HasModpackInfo(localPath) ? ModpackInfo.Load(localPath) : new();
if (updateInfos is not null && updateInfos.Updates.Count != 0)
{
var updatesOrderes = updateInfos.Updates.OrderByDescending(n => n.Version);
result.LatestVersion = updatesOrderes.First().Version;
result.CurrentVersion = modpackInfo.Version;
result.IsInstalled = true;
var checkingVersionIndex = 0;
var checkingVersion = updatesOrderes.ElementAtOrDefault(checkingVersionIndex);
while (checkingVersion is not null && checkingVersion.Version > result.CurrentVersion)
{
var actionsToAdd = new List<UpdateAction>();
foreach (var action in checkingVersion.Actions)
{
if (!result.Actions.Any(n => n.DestPath == action.DestPath))
actionsToAdd.Add(action);
}
result.Actions.InsertRange(0, actionsToAdd);
checkingVersionIndex += 1;
checkingVersion = updatesOrderes.ElementAtOrDefault(checkingVersionIndex);
}
}
else
result.HasError = true;
}
return result;
}
public async Task<bool?> Install(UpdateCheckResult checkResult)
{
ModpackInfo modpackInfo;
int processed = 0;
var localZipCache = new List<LocalZipCacheEntry>();
if (ModpackInfo.HasModpackInfo(localPath))
modpackInfo = ModpackInfo.Load(localPath);
else
modpackInfo = new();
foreach (InstallAction iaction in checkResult.Actions)
{
string destFilePath = Path.Combine(localPath, iaction.DestPath);
if (iaction is UpdateAction uaction)
{
switch (uaction.Type)
{
case UpdateActionType.Update:
await InstallFile(destFilePath, uaction.DownloadUrl, uaction.IsZip, uaction.ZipPath, localZipCache);
break;
case UpdateActionType.Delete:
{
if (uaction.IsDirectory)
{
if (Directory.Exists(destFilePath))
Directory.Delete(destFilePath, true);
}
else if (File.Exists(destFilePath))
File.Delete(destFilePath);
}
break;
case UpdateActionType.Copy:
{
var srcFilePath = Path.Combine(localPath, uaction.SrcPath);
if (uaction.IsDirectory)
{
if (Directory.Exists(srcFilePath))
Extensions.CopyDirectory(srcFilePath, destFilePath, true);
}
else if (File.Exists(srcFilePath))
{
Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
File.Copy(srcFilePath, destFilePath, true);
}
}
break;
case UpdateActionType.Move:
{
var srcFilePath = Path.Combine(localPath, uaction.SrcPath);
if (uaction.IsDirectory)
{
if (Directory.Exists(srcFilePath))
Directory.Move(srcFilePath, destFilePath);
}
else if (File.Exists(srcFilePath))
{
Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
File.Move(srcFilePath, destFilePath, true);
}
}
break;
}
}
else
await InstallFile(destFilePath, iaction.DownloadUrl, iaction.IsZip, iaction.ZipPath, localZipCache);
processed += 1;
InstallProgessUpdated?.Invoke(checkResult, processed);
}
// Save new modpack info
modpackInfo.Version = checkResult.LatestVersion;
modpackInfo.Save(localPath);
// Delete cached zip files
foreach (var task in localZipCache)
Directory.Delete(task.ExtractedZipPath, true);
return true;
}
private async Task InstallFile(string destFilePath, string sourceUrl, bool isZip, string zipPath, List<LocalZipCacheEntry> localZipCache)
{
Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
if (!isZip || localZipCache.FirstOrDefault(n => n.DownloadUrl == sourceUrl) is not LocalZipCacheEntry cachedZipInfo)
{
// Download
var fsDestinationPath = isZip ? Path.Combine(Path.GetTempPath(), $"mc_update_file_{DateTime.Now.ToBinary()}.zip") : destFilePath;
var sRemote = await httpClient.GetStreamAsync(sourceUrl);
var fs = new FileStream(fsDestinationPath, FileMode.Create, FileAccess.ReadWrite);
await sRemote.CopyToAsync(fs);
await fs.FlushAsync();
sRemote.Close();
fs.Close();
// Extract
if (isZip)
{
// Extract files
var zipDir = Path.Combine(Path.GetDirectoryName(fsDestinationPath), Path.GetFileNameWithoutExtension(fsDestinationPath));
ZipFile.ExtractToDirectory(fsDestinationPath, zipDir);
// Create cache entry
cachedZipInfo = new()
{
DownloadUrl = sourceUrl,
ExtractedZipPath = zipDir
};
localZipCache.Add(cachedZipInfo);
// Remofe temp zip file
File.Delete(fsDestinationPath);
}
else
cachedZipInfo = null;
}
// Handle zip file content
if (cachedZipInfo != null)
{
// Copy content
var zipSrc = Path.Combine(cachedZipInfo.ExtractedZipPath, zipPath);
Extensions.CopyDirectory(zipSrc, destFilePath, true);
}
}
}