231 lines
8.7 KiB
C#
231 lines
8.7 KiB
C#
using ModpackUpdater.Model;
|
|
using System.IO.Compression;
|
|
|
|
namespace ModpackUpdater.Manager;
|
|
|
|
public class ModpackInstaller(ModpackConfig updateConfig, ModpackInfo modpackInfo)
|
|
{
|
|
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(UpdateCheckOptions options)
|
|
{
|
|
InstallInfos installInfos = null;
|
|
UpdateInfos updateInfos = null;
|
|
var hasConfig = modpackInfo.Exists;
|
|
var result = new UpdateCheckResult();
|
|
|
|
if (updateConfig.Maintenance && !options.IgnoreMaintenance)
|
|
{
|
|
result.IsInMaintenance = true;
|
|
return result;
|
|
}
|
|
|
|
if (!hasConfig)
|
|
{
|
|
installInfos = await DownloadInstallInfos();
|
|
|
|
if (installInfos is not null && installInfos.Actions.Count != 0)
|
|
{
|
|
var actions = installInfos.Actions.Where(n => n.Side.IsSide(options.Side) && (!n.IsExtra || options.IncludeExtraActions));
|
|
if (actions.Any())
|
|
{
|
|
result.Actions.AddRange(installInfos.Actions);
|
|
result.LatestVersion = installInfos.Version;
|
|
}
|
|
}
|
|
|
|
if (result.Actions.Count == 0)
|
|
result.HasError = true;
|
|
}
|
|
|
|
if (options.AllowUpdaterAfterInstall)
|
|
{
|
|
updateInfos = await DownloadUpdateInfos();
|
|
|
|
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 (action.Side.IsSide(options.Side) && (!action.IsExtra || options.IncludeExtraActions) && !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)
|
|
{
|
|
int processed = 0;
|
|
var localZipCache = new List<LocalZipCacheEntry>();
|
|
|
|
foreach (InstallAction iaction in checkResult.Actions)
|
|
{
|
|
string destFilePath = Path.Combine(modpackInfo.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(modpackInfo.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(modpackInfo.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.ConfigUrl = updateConfig.ConfigUrl;
|
|
modpackInfo.Save();
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
} |