Esempio n. 1
0
        /// <summary>
        /// Installs a BYOND <paramref name="version"/> if it isn't already
        /// </summary>
        /// <param name="version">The BYOND <see cref="Version"/> to install</param>
        /// <param name="customVersionStream">Custom zip file <see cref="Stream"/> to use. Will cause a <see cref="Version.Build"/> number to be added.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task <string> InstallVersion(Version version, Stream customVersionStream, CancellationToken cancellationToken)
        {
            var    ourTcs = new TaskCompletionSource <object>();
            Task   inProgressTask;
            string versionKey;
            bool   installed;

            lock (installedVersions)
            {
                if (customVersionStream != null)
                {
                    int customInstallationNumber = 1;
                    do
                    {
                        versionKey = $"{VersionKey(version, false)}.{customInstallationNumber++}";
                    }while (installedVersions.ContainsKey(versionKey));
                }
                else
                {
                    versionKey = VersionKey(version, true);
                }

                installed = installedVersions.TryGetValue(versionKey, out inProgressTask);
                if (!installed)
                {
                    installedVersions.Add(versionKey, ourTcs.Task);
                }
            }

            if (installed)
            {
                using (cancellationToken.Register(() => ourTcs.SetCanceled()))
                {
                    await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();
                    return(versionKey);
                }
            }

            if (customVersionStream != null)
            {
                logger.LogInformation("Installing custom BYOND version as {0}...", versionKey);
            }
            else if (version.Build > 0)
            {
                throw new JobException(ErrorCode.ByondNonExistentCustomVersion);
            }
            else
            {
                logger.LogDebug("Requested BYOND version {0} not currently installed. Doing so now...");
            }

            // okay up to us to install it then
            try
            {
                await eventConsumer.HandleEvent(EventType.ByondInstallStart, new List <string> {
                    versionKey
                }, cancellationToken).ConfigureAwait(false);

                var extractPath = ioManager.ResolvePath(versionKey);
                async Task DirectoryCleanup()
                {
                    await ioManager.DeleteDirectory(extractPath, cancellationToken).ConfigureAwait(false);

                    await ioManager.CreateDirectory(extractPath, cancellationToken).ConfigureAwait(false);
                }

                var directoryCleanupTask = DirectoryCleanup();
                try
                {
                    Stream versionZipStream;
                    Stream downloadedStream = null;
                    if (customVersionStream == null)
                    {
                        var bytes = await byondInstaller.DownloadVersion(version, cancellationToken).ConfigureAwait(false);

                        downloadedStream = new MemoryStream(bytes);
                        versionZipStream = downloadedStream;
                    }
                    else
                    {
                        versionZipStream = customVersionStream;
                    }

                    using (downloadedStream)
                    {
                        await directoryCleanupTask.ConfigureAwait(false);

                        logger.LogTrace("Extracting downloaded BYOND zip to {0}...", extractPath);
                        await ioManager.ZipToDirectory(extractPath, versionZipStream, cancellationToken).ConfigureAwait(false);
                    }

                    await byondInstaller.InstallByond(extractPath, version, cancellationToken).ConfigureAwait(false);

                    // make sure to do this last because this is what tells us we have a valid version in the future
                    await ioManager.WriteAllBytes(
                        ioManager.ConcatPath(versionKey, VersionFileName),
                        Encoding.UTF8.GetBytes(versionKey),
                        cancellationToken)
                    .ConfigureAwait(false);
                }
                catch (WebException e)
                {
                    // since the user can easily provide non-exitent version numbers, we'll turn this into a JobException
                    throw new JobException(ErrorCode.ByondDownloadFail, e);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch
                {
                    await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                    throw;
                }

                ourTcs.SetResult(null);
            }
            catch (Exception e)
            {
                if (!(e is OperationCanceledException))
                {
                    await eventConsumer.HandleEvent(EventType.ByondInstallFail, new List <string> {
                        e.Message
                    }, cancellationToken).ConfigureAwait(false);
                }
                lock (installedVersions)
                    installedVersions.Remove(versionKey);
                ourTcs.SetException(e);
                throw;
            }

            return(versionKey);
        }
Esempio n. 2
0
        /// <summary>
        /// Installs a BYOND <paramref name="version"/> if it isn't already
        /// </summary>
        /// <param name="version">The BYOND <see cref="Version"/> to install</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task InstallVersion(Version version, CancellationToken cancellationToken)
        {
            var  ourTcs = new TaskCompletionSource <object>();
            Task inProgressTask;

            var  versionKey = VersionKey(version);
            bool installed;

            lock (installedVersions)
            {
                installed = installedVersions.TryGetValue(versionKey, out inProgressTask);
                if (!installed)
                {
                    installedVersions.Add(versionKey, ourTcs.Task);
                }
            }

            if (installed)
            {
                using (cancellationToken.Register(() => ourTcs.SetCanceled()))
                {
                    await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();
                    return;
                }
            }
            else
            {
                logger.LogDebug("Requested BYOND version {0} not currently installed. Doing so now...");
            }

            // okay up to us to install it then
            try
            {
                await eventConsumer.HandleEvent(EventType.ByondInstallStart, new List <string> {
                    versionKey
                }, cancellationToken).ConfigureAwait(false);

                var downloadTask = byondInstaller.DownloadVersion(version, cancellationToken);

                await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                try
                {
                    var download = await downloadTask.ConfigureAwait(false);

                    await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                    var extractPath = ioManager.ResolvePath(versionKey);
                    logger.LogTrace("Extracting downloaded BYOND zip to {0}...", extractPath);
                    await ioManager.ZipToDirectory(extractPath, download, cancellationToken).ConfigureAwait(false);

                    await byondInstaller.InstallByond(extractPath, version, cancellationToken).ConfigureAwait(false);

                    // make sure to do this last because this is what tells us we have a valid version in the future
                    await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
                }
                catch (WebException e)
                {
                    // since the user can easily provide non-exitent version numbers, we'll turn this into a JobException
                    throw new JobException(ErrorCode.ByondDownloadFail, e);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch
                {
                    await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                    throw;
                }

                ourTcs.SetResult(null);
            }
            catch (Exception e)
            {
                if (!(e is OperationCanceledException))
                {
                    await eventConsumer.HandleEvent(EventType.ByondInstallFail, new List <string> {
                        e.Message
                    }, cancellationToken).ConfigureAwait(false);
                }
                lock (installedVersions)
                    installedVersions.Remove(versionKey);
                ourTcs.SetException(e);
                throw;
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Installs a BYOND <paramref name="version"/> if it isn't already
        /// </summary>
        /// <param name="version">The BYOND <see cref="Version"/> to install</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        async Task InstallVersion(Version version, CancellationToken cancellationToken)
        {
            var  ourTcs = new TaskCompletionSource <object>();
            Task inProgressTask;

            var  versionKey = VersionKey(version);
            bool installed;

            lock (installedVersions)
            {
                installed = installedVersions.TryGetValue(versionKey, out inProgressTask);
                if (!installed)
                {
                    installedVersions.Add(versionKey, ourTcs.Task);
                }
            }
            if (installed)
            {
                using (cancellationToken.Register(() => ourTcs.SetCanceled()))
                {
                    await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false);

                    cancellationToken.ThrowIfCancellationRequested();
                    return;
                }
            }
            try
            {
                var downloadTask = byondInstaller.DownloadVersion(version, cancellationToken);

                //okay up to us to install it then
                await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                try
                {
                    //byond can just decide to corrupt the zip fnr
                    //(or maybe our downloader is a s***e)
                    //either way try a few times
                    for (var I = 0; I < 3; ++I)
                    {
                        var download = await downloadTask.ConfigureAwait(false);

                        try
                        {
                            await ioManager.ZipToDirectory(versionKey, download, cancellationToken).ConfigureAwait(false);

                            break;
                        }
                        catch (OperationCanceledException)
                        {
                            throw;
                        }
                        catch
                        {
                            if (I == 2)
                            {
                                throw;
                            }
                            downloadTask = byondInstaller.DownloadVersion(version, cancellationToken);
                        }
                    }
                    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 in the future
                    await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch
                {
                    await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);

                    throw;
                }

                ourTcs.SetResult(null);
            }
            catch (Exception e)
            {
                lock (installedVersions)
                    installedVersions.Remove(versionKey);
                ourTcs.SetException(e);
                throw;
            }
        }