mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-27 15:07:03 +01:00
Create a truly async ZipToDirectory
This commit is contained in:
@@ -12,13 +12,13 @@ namespace Tgstation.Server.Host.Startup
|
||||
/// <summary>
|
||||
/// The path to the updated assembly to run if any. Populated once <see cref="RunAsync(CancellationToken)"/> returns
|
||||
/// </summary>
|
||||
Guid UpdateGuid { get; }
|
||||
Guid? UpdateGuid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs the <see cref="IServer"/>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task RunAsync(string updatesPath, CancellationToken cancellationToken);
|
||||
Task RunAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
/// Create a <see cref="IServer"/>
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments for the <see cref="IServer"/></param>
|
||||
/// <param name="updatePath">The directory in which to install server updates</param>
|
||||
/// <returns>A new <see cref="IServer"/></returns>
|
||||
IServer CreateServer(string[] args);
|
||||
IServer CreateServer(string[] args, string updatePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,9 @@ namespace Tgstation.Server.Host.Components
|
||||
await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
|
||||
await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var resolvedPath = ioManager.ResolvePath(versionKey);
|
||||
using (var zipBytes = new MemoryStream(await downloadTask.ConfigureAwait(false)))
|
||||
using (var archive = new ZipArchive(zipBytes))
|
||||
await Task.Factory.StartNew(() => archive.ExtractToDirectory(resolvedPath), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
|
||||
await ioManager.ZipToDirectory(versionKey, await downloadTask.ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await byondInstaller.InstallByond(resolvedPath, version, cancellationToken).ConfigureAwait(false);
|
||||
await byondInstaller.InstallByond(ioManager.ResolvePath(versionKey), version, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
//make sure to do this last because this is what tells us we have a valid version
|
||||
await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
@@ -10,8 +13,11 @@ namespace Tgstation.Server.Host.Core
|
||||
/// <summary>
|
||||
/// Run a new <see cref="Host"/> assembly and stop the current one. This will likely trigger all active <see cref="System.Threading.CancellationToken"/>s
|
||||
/// </summary>
|
||||
/// <param name="updatePath">The path to the new <see cref="Host"/> assembly</param>
|
||||
void ApplyUpdate(string updatePath);
|
||||
/// <param name="updateZipData">The <see cref="byte"/>s of the .zip file that contains the new <see cref="Host"/> assembly</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <param name="ioManager">The <see cref="IIOManager"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Register a given <paramref name="action"/> to run before stopping the server for updates
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
@@ -46,6 +47,12 @@ namespace Tgstation.Server.Host.IO
|
||||
dir.Delete(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a <see cref="FileStream"/> for async writing at a given <paramref name="path"/>
|
||||
/// </summary>
|
||||
/// <param name="path">The path to open the <see cref="FileStream"/> at</param>
|
||||
/// <returns>A new <see cref="FileStream"/> ready for async writing</returns>
|
||||
static FileStream OpenWriteStream(string path) => new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, true);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a directory from <paramref name="src"/> to <paramref name="dest"/>
|
||||
@@ -205,7 +212,7 @@ namespace Tgstation.Server.Host.IO
|
||||
public async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
using (var file = File.Open(path, FileMode.Create, FileAccess.Write))
|
||||
using (var file = OpenWriteStream(path))
|
||||
await file.WriteAsync(contents, 0, contents.Length, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -252,5 +259,32 @@ namespace Tgstation.Server.Host.IO
|
||||
using (cancellationToken.Register(() => wc.CancelAsync()))
|
||||
return await wc.DownloadDataTaskAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken)
|
||||
{
|
||||
path = ResolvePath(path);
|
||||
if (zipFileBytes == null)
|
||||
throw new ArgumentNullException(nameof(zipFileBytes));
|
||||
|
||||
using (var ms = new MemoryStream(zipFileBytes))
|
||||
{
|
||||
zipFileBytes = null;
|
||||
using (var archive = new ZipArchive(ms))
|
||||
{
|
||||
string GetEntryName(ZipArchiveEntry entry) => ConcatPath(path, GetDirectoryName(entry.FullName));
|
||||
//create directories first
|
||||
await Task.WhenAll(archive.Entries.Where(x => x.Name.Length == 0).Select(x => CreateDirectory(GetEntryName(x), cancellationToken))).ConfigureAwait(false);
|
||||
//extract files
|
||||
await Task.WhenAll(archive.Entries.Where(x => x.Name.Length > 0).Select(async x =>
|
||||
{
|
||||
var entryPath = GetEntryName(x);
|
||||
using (var stream = x.Open())
|
||||
using (var file = OpenWriteStream(entryPath))
|
||||
await stream.CopyToAsync(file).ConfigureAwait(false);
|
||||
})).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,5 +162,14 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="byte"/>s of the downloaded file</returns>
|
||||
Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Extract a set of <paramref name="zipFileBytes"/> to a given <paramref name="path"/>
|
||||
/// </summary>
|
||||
/// <param name="path">The path to unzip to</param>
|
||||
/// <param name="zipFileBytes">The <see cref="byte"/>s of the <see cref="System.IO.Compression.ZipArchive"/></param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +75,9 @@ namespace Tgstation.Server.Host
|
||||
UpdateGuid = Guid.NewGuid();
|
||||
try
|
||||
{
|
||||
await ioManager.WriteAllBytes(serverUpdatePath, updateData, cancellationToken).ConfigureAwait(false);
|
||||
await ioManager.ZipToDirectory(updatePath, updateZipData, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
catch
|
||||
{
|
||||
UpdateGuid = null;
|
||||
throw;
|
||||
|
||||
Reference in New Issue
Block a user