diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm
index 6187a67825a..6d35cf6593e 100644
--- a/src/DMAPI/tgs.dm
+++ b/src/DMAPI/tgs.dm
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
-#define TGS_DMAPI_VERSION "6.5.3"
+#define TGS_DMAPI_VERSION "6.6.0"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -73,11 +73,11 @@
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path.
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
-/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND.
+/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND, engine type of the installing BYOND.
#define TGS_EVENT_BYOND_INSTALL_START 5
/// When a BYOND install operation fails. Parameters: Error message
#define TGS_EVENT_BYOND_INSTALL_FAIL 6
-/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND.
+/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND, engine type of the current BYOND, engine type of the new BYOND.
#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA.
#define TGS_EVENT_COMPILE_START 8
@@ -129,6 +129,11 @@
/// DreamDaemon Ultrasafe security level.
#define TGS_SECURITY_ULTRASAFE 2
+/// The Build Your Own Net Dream engine.
+#define TGS_ENGINE_TYPE_BYOND 0
+/// The OpenDream engine.
+#define TGS_ENGINE_TYPE_OPENDREAM 1
+
//REQUIRED HOOKS
/**
diff --git a/src/Tgstation.Server.Api/Models/EngineType.cs b/src/Tgstation.Server.Api/Models/EngineType.cs
new file mode 100644
index 00000000000..67410c1d470
--- /dev/null
+++ b/src/Tgstation.Server.Api/Models/EngineType.cs
@@ -0,0 +1,18 @@
+namespace Tgstation.Server.Api.Models
+{
+ ///
+ /// The type of engine the codebase is using.
+ ///
+ public enum EngineType
+ {
+ ///
+ /// Build your own net dream,
+ ///
+ Byond,
+
+ ///
+ /// The OpenDream BYOND reimplementation.
+ ///
+ OpenDream,
+ }
+}
diff --git a/src/Tgstation.Server.Api/Models/Internal/ByondVersion.cs b/src/Tgstation.Server.Api/Models/Internal/ByondVersion.cs
new file mode 100644
index 00000000000..bed173ed8fe
--- /dev/null
+++ b/src/Tgstation.Server.Api/Models/Internal/ByondVersion.cs
@@ -0,0 +1,108 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+
+namespace Tgstation.Server.Api.Models.Internal
+{
+ ///
+ /// Information about a Byond installation.
+ ///
+ public class ByondVersion : IEquatable
+ {
+ ///
+ /// The .
+ ///
+ [RequestOptions(FieldPresence.Required)]
+ public EngineType? Engine { get; set; }
+
+ ///
+ /// The of the engine.
+ ///
+ [RequestOptions(FieldPresence.Required)]
+ public Version? Version { get; set; }
+
+ ///
+ /// The git committish of the . On response, this will always be a commit SHA.
+ ///
+ [ResponseOptions]
+ [StringLength(Limits.MaximumCommitShaLength)]
+ public string? SourceCommittish { get; set; }
+
+ ///
+ /// Parses a stringified .
+ ///
+ /// The input .
+ /// The output .
+ /// if parsing was successful, otherwise.
+ public static bool TryParse(string input, out ByondVersion byondVersion)
+ {
+ if (input == null)
+ throw new ArgumentNullException(nameof(input));
+
+ var splits = input.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
+ byondVersion = new ByondVersion();
+
+ if (splits.Length > 2)
+ return false;
+
+ EngineType engine;
+ if (splits.Length > 1)
+ {
+ if (!Enum.TryParse(splits[0], out engine))
+ return false;
+ }
+ else
+ engine = EngineType.Byond;
+
+ byondVersion.Engine = engine;
+
+ if (!Version.TryParse(splits.Last(), out var version))
+ return false;
+
+ byondVersion.Version = version;
+
+ return true;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ByondVersion()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The to copy.
+ public ByondVersion(ByondVersion other)
+ {
+ if (other == null)
+ throw new ArgumentNullException(nameof(other));
+
+ Version = other.Version;
+ Engine = other.Engine;
+ SourceCommittish = other.SourceCommittish;
+ }
+
+ ///
+ public bool Equals(ByondVersion other)
+ {
+ // https://github.com/dotnet/roslyn-analyzers/issues/2875
+#pragma warning disable CA1062 // Validate arguments of public methods
+ return other!.Version == Version
+ && other.Engine == Engine;
+#pragma warning restore CA1062 // Validate arguments of public methods
+ }
+
+ ///
+ public override bool Equals(object obj)
+ => obj is ByondVersion other && Equals(other);
+
+ ///
+ public override string ToString() => $"{(Engine != EngineType.Byond ? $"{Engine}-" : String.Empty)}{Version}"; // BYOND isn't display for backwards compatibility. SourceCommittish is not included
+
+ ///
+ public override int GetHashCode() => ToString().GetHashCode();
+ }
+}
diff --git a/src/Tgstation.Server.Api/Models/Request/ByondVersionDeleteRequest.cs b/src/Tgstation.Server.Api/Models/Request/ByondVersionDeleteRequest.cs
index aaf8585c9be..ad5eac45ccd 100644
--- a/src/Tgstation.Server.Api/Models/Request/ByondVersionDeleteRequest.cs
+++ b/src/Tgstation.Server.Api/Models/Request/ByondVersionDeleteRequest.cs
@@ -1,16 +1,13 @@
using System;
+using Tgstation.Server.Api.Models.Internal;
+
namespace Tgstation.Server.Api.Models.Request
{
///
/// A request to delete a specific .
///
- public class ByondVersionDeleteRequest
+ public class ByondVersionDeleteRequest : ByondVersion
{
- ///
- /// The BYOND version to install.
- ///
- [RequestOptions(FieldPresence.Required)]
- public Version? Version { get; set; }
}
}
diff --git a/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs b/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs
index b86eea074a9..a04dfd0d7e1 100644
--- a/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs
+++ b/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs
@@ -1,13 +1,22 @@
-namespace Tgstation.Server.Api.Models.Request
+using System;
+
+using Tgstation.Server.Api.Models.Internal;
+
+namespace Tgstation.Server.Api.Models.Request
{
///
- /// A request to install a BYOND .
+ /// A request to install a .
///
- public sealed class ByondVersionRequest : ByondVersionDeleteRequest
+ public sealed class ByondVersionRequest : ByondVersion
{
///
/// If a custom BYOND version is to be uploaded.
///
public bool? UploadCustomZip { get; set; }
+
+ ///
+ /// The remote repository for non- s. By default, this is the original git repository of the target .
+ ///
+ public Uri? SourceRepository { get; set; }
}
}
diff --git a/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs b/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs
index 64b01090d53..92d524728da 100644
--- a/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs
+++ b/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs
@@ -1,16 +1,19 @@
-using System;
+using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Api.Models.Response
{
///
- /// Represents an installed BYOND .
+ /// Represents an installed .
///
- public sealed class ByondResponse
+ public sealed class ByondResponse : ByondVersion
{
///
- /// The installed BYOND . BYOND itself only considers the and numbers. TGS uses the number to represent installed custom versions.
+ /// Initializes a new instance of the class.
///
- [ResponseOptions]
- public Version? Version { get; set; }
+ /// The to copy.
+ public ByondResponse(ByondVersion byondVersion)
+ : base(byondVersion)
+ {
+ }
}
}
diff --git a/src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs b/src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs
index 60444d9c3c9..e7fc06c3b21 100644
--- a/src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs
+++ b/src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs
@@ -16,10 +16,15 @@ public sealed class CompileJobResponse : Internal.CompileJob
public RevisionInformation? RevisionInformation { get; set; }
///
- /// The the was made with.
+ /// The the was made with.
///
public Version? ByondVersion { get; set; }
+ ///
+ /// The the was made with.
+ ///
+ public EngineType? Engine { get; set; }
+
///
/// The origin of the repository the compile job was built from.
///
diff --git a/src/Tgstation.Server.Api/Rights/ByondRights.cs b/src/Tgstation.Server.Api/Rights/ByondRights.cs
index 288d8ca3b8d..6e20a62b3b5 100644
--- a/src/Tgstation.Server.Api/Rights/ByondRights.cs
+++ b/src/Tgstation.Server.Api/Rights/ByondRights.cs
@@ -14,33 +14,43 @@ public enum ByondRights : ulong
None = 0,
///
- /// User may view the active installed BYOND version.
+ /// User may view the active installed engine versions.
///
ReadActive = 1 << 0,
///
- /// User may list all installed BYOND versions.
+ /// User may list all installed engine versions.
///
ListInstalled = 1 << 1,
///
- /// User may install official BYOND versions or change the active BYOND version.
+ /// User may install official versions or change the active version.
///
- InstallOfficialOrChangeActiveVersion = 1 << 2,
+ InstallOfficialOrChangeActiveByondVersion = 1 << 2,
///
- /// User may cancel BYOND installation job.
+ /// User may cancel an engine installation job.
///
CancelInstall = 1 << 3,
///
- /// User may upload and activate custom BYOND builds.
+ /// User may upload and activate custom builds.
///
- InstallCustomVersion = 1 << 4,
+ InstallCustomByondVersion = 1 << 4,
///
- /// User may delete non-active BYOND builds.
+ /// User may delete non-active engine builds.
///
DeleteInstall = 1 << 5,
+
+ ///
+ /// User may install official versions or change the active version.
+ ///
+ InstallOfficialOrChangeActiveOpenDreamVersion = 1 << 6,
+
+ ///
+ /// User may activate custom builds via zip upload or custom git committish.
+ ///
+ InstallCustomOpenDreamVersion = 1 << 7,
}
}
diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs b/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs
index 334b3469ea1..71391f1d7f8 100644
--- a/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs
@@ -1,5 +1,9 @@
using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Utils;
namespace Tgstation.Server.Host.Components.Byond
@@ -8,21 +12,36 @@ namespace Tgstation.Server.Host.Components.Byond
sealed class ByondExecutableLock : ReferenceCounter, IByondExecutableLock
{
///
- public Version Version => Instance.Version;
+ public ByondVersion Version => Instance.Version;
///
- public string DreamDaemonPath => Instance.DreamDaemonPath;
+ public string ServerExePath => Instance.ServerExePath;
///
- public string DreamMakerPath => Instance.DreamMakerPath;
+ public string CompilerExePath => Instance.CompilerExePath;
///
- public bool SupportsCli => Instance.SupportsCli;
+ public bool HasStandardOutput => Instance.HasStandardOutput;
///
- public bool SupportsMapThreads => Instance.SupportsMapThreads;
+ public bool PromptsForNetworkAccess => Instance.PromptsForNetworkAccess;
+
+ ///
+ public Task InstallationTask => Instance.InstallationTask;
///
public void DoNotDeleteThisSession() => DangerousDropReference();
+
+ ///
+ public string FormatServerArguments(
+ IDmbProvider dmbProvider,
+ IReadOnlyDictionary parameters,
+ DreamDaemonLaunchParameters launchParameters,
+ string logFilePath)
+ => Instance.FormatServerArguments(
+ dmbProvider,
+ parameters,
+ launchParameters,
+ logFilePath);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondInstallation.cs b/src/Tgstation.Server.Host/Components/Byond/ByondInstallation.cs
index 8554190d0f6..74b537c0fa3 100644
--- a/src/Tgstation.Server.Host/Components/Byond/ByondInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/ByondInstallation.cs
@@ -1,5 +1,13 @@
using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
using System.Threading.Tasks;
+using System.Web;
+
+using Tgstation.Server.Api.Models;
+using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Host.Components.Deployment;
namespace Tgstation.Server.Host.Components.Byond
{
@@ -7,48 +15,125 @@ namespace Tgstation.Server.Host.Components.Byond
sealed class ByondInstallation : IByondInstallation
{
///
- public Version Version { get; }
+ public ByondVersion Version { get; }
///
- public string DreamDaemonPath { get; }
+ public string ServerExePath { get; }
///
- public string DreamMakerPath { get; }
+ public string CompilerExePath { get; }
///
- public bool SupportsCli { get; }
+ public bool PromptsForNetworkAccess { get; }
///
- public bool SupportsMapThreads { get; }
+ public bool HasStandardOutput { get; }
+
+ ///
+ public Task InstallationTask { get; }
///
- /// The that completes when the BYOND version finished installing.
+ /// Change a given into the appropriate DreamDaemon command line word.
///
- public Task InstallationTask { get; }
+ /// The level to change.
+ /// A representation of the command line parameter.
+ static string SecurityWord(DreamDaemonSecurity securityLevel)
+ {
+ return securityLevel switch
+ {
+ DreamDaemonSecurity.Safe => "safe",
+ DreamDaemonSecurity.Trusted => "trusted",
+ DreamDaemonSecurity.Ultrasafe => "ultrasafe",
+ _ => throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel)),
+ };
+ }
+
+ ///
+ /// Change a given into the appropriate DreamDaemon command line word.
+ ///
+ /// The level to change.
+ /// A representation of the command line parameter.
+ static string VisibilityWord(DreamDaemonVisibility visibility)
+ {
+ return visibility switch
+ {
+ DreamDaemonVisibility.Public => "public",
+ DreamDaemonVisibility.Private => "private",
+ DreamDaemonVisibility.Invisible => "invisible",
+ _ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon visibility level: {0}", visibility)),
+ };
+ }
///
/// Initializes a new instance of the class.
///
/// The value of .
/// The value of .
- /// The value of .
- /// The value of .
- /// The value of .
- /// The value of .
+ /// The value of .
+ /// The value of .
+ /// If a CLI application is being used.
public ByondInstallation(
Task installationTask,
- Version version,
+ ByondVersion version,
string dreamDaemonPath,
string dreamMakerPath,
- bool supportsCli,
- bool supportsMapThreads)
+ bool supportsCli)
{
InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
Version = version ?? throw new ArgumentNullException(nameof(version));
- DreamDaemonPath = dreamDaemonPath ?? throw new ArgumentNullException(nameof(dreamDaemonPath));
- DreamMakerPath = dreamMakerPath ?? throw new ArgumentNullException(nameof(dreamMakerPath));
- SupportsCli = supportsCli;
- SupportsMapThreads = supportsMapThreads;
+ ServerExePath = dreamDaemonPath ?? throw new ArgumentNullException(nameof(dreamDaemonPath));
+ CompilerExePath = dreamMakerPath ?? throw new ArgumentNullException(nameof(dreamMakerPath));
+ HasStandardOutput = supportsCli;
+ PromptsForNetworkAccess = !supportsCli;
+ }
+
+ ///
+ public string FormatServerArguments(
+ IDmbProvider dmbProvider,
+ IReadOnlyDictionary parameters,
+ DreamDaemonLaunchParameters launchParameters,
+ string logFilePath)
+ {
+ ArgumentNullException.ThrowIfNull(dmbProvider);
+ ArgumentNullException.ThrowIfNull(parameters);
+ ArgumentNullException.ThrowIfNull(launchParameters);
+
+ switch (Version.Engine.Value)
+ {
+ case EngineType.Byond:
+ var parametersString = String.Join('&', parameters.Select(kvp => $"{HttpUtility.UrlEncode(kvp.Key)}={HttpUtility.UrlEncode(kvp.Value)}"));
+
+ if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
+ parametersString = $"{parametersString}&{launchParameters.AdditionalParameters}";
+
+ var supportsMapThreads = Version.Version >= ByondInstallerBase.MapThreadsVersion;
+ var arguments = String.Format(
+ CultureInfo.InvariantCulture,
+ "{0} -port {1} -ports 1-65535 {2}-close -verbose -{3} -{4}{5}{6}{7} -params \"{8}\"",
+ dmbProvider.DmbName,
+ launchParameters.Port.Value,
+ launchParameters.AllowWebClient.Value
+ ? "-webclient "
+ : String.Empty,
+ SecurityWord(launchParameters.SecurityLevel.Value),
+ VisibilityWord(launchParameters.Visibility.Value),
+ logFilePath != null
+ ? $" -logself -log {logFilePath}"
+ : String.Empty, // DD doesn't output anything if -logself is set???
+ launchParameters.StartProfiler.Value
+ ? " -profile"
+ : String.Empty,
+ supportsMapThreads && launchParameters.MapThreads.Value != 0
+ ? $" -map-threads {launchParameters.MapThreads.Value}"
+ : String.Empty,
+ parametersString);
+ return arguments;
+ case EngineType.OpenDream:
+ throw new NotImplementedException();
+ default:
+ throw new InvalidOperationException($"Invalid EngineType: {Version.Engine.Value}");
+ }
+
}
}
}
diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs
index 2478acdc1f7..8260eafffaa 100644
--- a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs
@@ -6,6 +6,7 @@
using Microsoft.Extensions.Logging;
+using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components.Byond
@@ -24,10 +25,10 @@ abstract class ByondInstallerBase : IByondInstaller
public static Version MapThreadsVersion => new (515, 1609);
///
- public abstract string DreamMakerName { get; }
+ public abstract string CompilerName { get; }
///
- public abstract string PathToUserByondFolder { get; }
+ public abstract string PathToUserFolder { get; }
///
/// Gets the URL formatter string for downloading a byond version of {0:Major} {1:Minor}.
@@ -63,7 +64,7 @@ protected ByondInstallerBase(IIOManager ioManager, IFileDownloader fileDownloade
}
///
- public abstract string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads);
+ public abstract string GetDreamDaemonName(ByondVersion version, out bool supportsCli, out bool supportsMapThreads);
///
public async Task CleanCache(CancellationToken cancellationToken)
@@ -73,7 +74,7 @@ public async Task CleanCache(CancellationToken cancellationToken)
Logger.LogDebug("Cleaning BYOND cache...");
await IOManager.DeleteDirectory(
IOManager.ConcatPath(
- PathToUserByondFolder,
+ PathToUserFolder,
CacheDirectoryName),
cancellationToken);
}
@@ -84,18 +85,18 @@ await IOManager.DeleteDirectory(
}
///
- public abstract ValueTask InstallByond(Version version, string path, CancellationToken cancellationToken);
+ public abstract ValueTask InstallByond(ByondVersion version, string path, CancellationToken cancellationToken);
///
- public abstract ValueTask UpgradeInstallation(Version version, string path, CancellationToken cancellationToken);
+ public abstract ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken);
///
- public async ValueTask DownloadVersion(Version version, CancellationToken cancellationToken)
+ public async ValueTask DownloadVersion(ByondVersion version, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(version);
- Logger.LogTrace("Downloading BYOND version {major}.{minor}...", version.Major, version.Minor);
- var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Major, version.Minor);
+ Logger.LogTrace("Downloading BYOND version {major}.{minor}...", version.Version.Major, version.Version.Minor);
+ var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Version.Major, version.Version.Minor);
await using var download = fileDownloader.DownloadFile(new Uri(url), null);
await using var buffer = new BufferedFileStreamProvider(
diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs b/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs
index 73fadec9874..24eb916b265 100644
--- a/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs
@@ -10,6 +10,7 @@
using Microsoft.Extensions.Logging;
using Tgstation.Server.Api.Models;
+using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Host.Components.Events;
using Tgstation.Server.Host.IO;
@@ -47,10 +48,10 @@ sealed class ByondManager : IByondManager
const string ActiveVersionFileName = "ActiveVersion.txt";
///
- public Version ActiveVersion { get; private set; }
+ public ByondVersion ActiveVersion { get; private set; }
///
- public IReadOnlyList InstalledVersions
+ public IReadOnlyList InstalledVersions
{
get
{
@@ -87,7 +88,7 @@ public IReadOnlyList InstalledVersions
///
/// Map of byond s to s that complete when they are installed.
///
- readonly Dictionary> installedVersions;
+ readonly Dictionary> installedVersions;
///
/// The for changing or deleting the active BYOND version.
@@ -103,14 +104,29 @@ public IReadOnlyList InstalledVersions
/// Validates a given parameter.
///
/// The to validate.
- static void CheckVersionParameter(Version version)
+ static void CheckVersionParameter(ByondVersion version)
{
ArgumentNullException.ThrowIfNull(version);
- if (version.Build == 0)
+ if (!version.Engine.HasValue)
+ throw new InvalidOperationException("version.Engine cannot be null!");
+
+ if (version.Version.Build == 0)
throw new InvalidOperationException("version.Build cannot be 0!");
}
+ ///
+ /// Parses a string
+ ///
+ ///
+ ///
+ ///
+ ///
+ static bool TryParseByondVersion(string input, out ByondVersion byondVersion)
+ {
+ throw new NotImplementedException();
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -125,7 +141,7 @@ public ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, IEvent
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- installedVersions = new Dictionary>();
+ installedVersions = new Dictionary>();
changeDeleteSemaphore = new SemaphoreSlim(1);
activeVersionChanged = new TaskCompletionSource();
}
@@ -136,7 +152,7 @@ public ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, IEvent
///
public async ValueTask ChangeVersion(
JobProgressReporter progressReporter,
- Version version,
+ ByondVersion version,
Stream customVersionStream,
bool allowInstallation,
CancellationToken cancellationToken)
@@ -154,7 +170,10 @@ public async ValueTask ChangeVersion(
cancellationToken);
// We reparse the version because it could be changed after a custom install.
- version = installLock.Version;
+ version = new ByondVersion(version)
+ {
+ Version = installLock.Version,
+ };
var stringVersion = version.ToString();
await ioManager.WriteAllBytes(ActiveVersionFileName, Encoding.UTF8.GetBytes(stringVersion), cancellationToken);
@@ -177,7 +196,7 @@ await eventConsumer.HandleEvent(
}
///
- public async ValueTask UseExecutables(Version requiredVersion, string trustDmbFullPath, CancellationToken cancellationToken)
+ public async ValueTask UseExecutables(ByondVersion requiredVersion, string trustDmbFullPath, CancellationToken cancellationToken)
{
logger.LogTrace(
"Acquiring lock on BYOND version {version}...",
@@ -205,7 +224,7 @@ public async ValueTask UseExecutables(Version requiredVers
}
///
- public async ValueTask DeleteVersion(JobProgressReporter progressReporter, Version version, CancellationToken cancellationToken)
+ public async ValueTask DeleteVersion(JobProgressReporter progressReporter, ByondVersion version, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(progressReporter);
@@ -213,15 +232,18 @@ public async ValueTask DeleteVersion(JobProgressReporter progressReporter, Versi
logger.LogTrace("DeleteVersion {version}", version);
- if (version == ActiveVersion)
+ if (version.Equals(ActiveVersion))
throw new JobException(ErrorCode.ByondCannotDeleteActiveVersion);
- ReferenceCountingContainer container;
+ ReferenceCountingContainer container;
lock (installedVersions)
if (!installedVersions.TryGetValue(version, out container))
- return; // already "deleted"
+ {
+ logger.LogTrace("Version {version} already deleted.", version);
+ return;
+ }
- logger.LogInformation("Deleting BYOND version {version}...", version);
+ logger.LogInformation("Deleting version {version}...", version);
progressReporter.StageName = "Waiting for version to not be in use...";
while (true)
{
@@ -238,7 +260,7 @@ await Task.WhenAny(
.WaitAsync(cancellationToken);
if (containerTask.IsCompleted)
- logger.LogTrace("All BYOND locks for {version} are gone", version);
+ logger.LogTrace("All locks for version {version} are gone", version);
using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
{
@@ -252,7 +274,7 @@ await Task.WhenAny(
proceed = container.OnZeroReferences.IsCompleted;
if (proceed)
if (!installedVersions.TryGetValue(version, out var newerContainer))
- logger.LogWarning("Unable to remove BYOND installation {version} from list! Is there a duplicate job running?", version);
+ logger.LogWarning("Unable to remove engine installation {version} from list! Is there a duplicate job running?", version);
else
{
if (container != newerContainer)
@@ -302,7 +324,7 @@ async ValueTask GetActiveVersion()
var activeVersionBytesTask = GetActiveVersion();
- var byondDir = byondInstaller.PathToUserByondFolder;
+ var byondDir = byondInstaller.PathToUserFolder;
if (byondDir != null)
using (await SemaphoreSlimContext.Lock(UserFilesSemaphore, cancellationToken))
{
@@ -328,7 +350,7 @@ await ioManager.DeleteFile(
await ioManager.CreateDirectory(DefaultIOManager.CurrentDirectory, cancellationToken);
var directories = await ioManager.GetDirectories(DefaultIOManager.CurrentDirectory, cancellationToken);
- var installedVersionPaths = new Dictionary();
+ var installedVersionPaths = new Dictionary();
async ValueTask ReadVersion(string path)
{
@@ -342,7 +364,7 @@ async ValueTask ReadVersion(string path)
var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken);
var text = Encoding.UTF8.GetString(bytes);
- if (!Version.TryParse(text, out var version))
+ if (!TryParseByondVersion(text, out var version))
{
logger.LogWarning("Cleaning path with unparsable version file: {versionPath}", ioManager.ResolvePath(path));
await ioManager.DeleteDirectory(path, cancellationToken); // cleanup
@@ -383,10 +405,10 @@ await ValueTaskExtensions.WhenAll(
{
var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes);
- Version activeVersion;
+ ByondVersion activeVersion;
bool hasRequestedActiveVersion;
lock (installedVersions)
- hasRequestedActiveVersion = Version.TryParse(activeVersionString, out activeVersion)
+ hasRequestedActiveVersion = TryParseByondVersion(activeVersionString, out activeVersion)
&& installedVersions.ContainsKey(activeVersion);
if (hasRequestedActiveVersion)
@@ -403,46 +425,47 @@ await ValueTaskExtensions.WhenAll(
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
///
- /// Ensures a BYOND is installed if it isn't already.
+ /// Ensures a BYOND is installed if it isn't already.
///
/// The optional for the operation.
- /// The BYOND to install.
+ /// The to install.
/// Custom zip file to use. Will cause a number to be added.
/// If this BYOND version is required as part of a locking operation.
- /// If an installation should be performed if the is not installed. If and an installation is required an will be thrown.
+ /// If an installation should be performed if the is not installed. If and an installation is required an will be thrown.
/// The for the operation.
/// A resulting in the .
async ValueTask AssertAndLockVersion(
JobProgressReporter progressReporter,
- Version version,
+ ByondVersion byondVersion,
Stream customVersionStream,
bool neededForLock,
bool allowInstallation,
CancellationToken cancellationToken)
{
var ourTcs = new TaskCompletionSource();
- ByondInstallation installation;
+ IByondInstallation installation;
ByondExecutableLock installLock;
bool installedOrInstalling;
+ var byondEngine = byondVersion.Engine.Value == EngineType.Byond;
lock (installedVersions)
{
- if (customVersionStream != null)
+ if (customVersionStream != null && byondEngine)
{
var customInstallationNumber = 1;
do
{
- version = new Version(version.Major, version.Minor, customInstallationNumber++);
+ byondVersion.Version = new Version(byondVersion.Version.Major, byondVersion.Version.Minor, customInstallationNumber++);
}
- while (installedVersions.ContainsKey(version));
+ while (installedVersions.ContainsKey(byondVersion));
}
- installedOrInstalling = installedVersions.TryGetValue(version, out var installationContainer);
+ installedOrInstalling = installedVersions.TryGetValue(byondVersion, out var installationContainer);
if (!installedOrInstalling)
{
if (!allowInstallation)
- throw new InvalidOperationException($"BYOND version {version} not installed!");
+ throw new InvalidOperationException($"BYOND version {byondVersion} not installed!");
- installationContainer = AddInstallationContainer(version, ourTcs.Task);
+ installationContainer = AddInstallationContainer(byondVersion, ourTcs.Task);
}
installation = installationContainer.Instance;
@@ -457,7 +480,7 @@ async ValueTask AssertAndLockVersion(
progressReporter.StageName = "Waiting for existing installation job...";
if (neededForLock && !installation.InstallationTask.IsCompleted)
- logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to wait for it to install.", version);
+ logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to wait for it to install.", byondVersion);
await installation.InstallationTask.WaitAsync(cancellationToken);
return installLock;
@@ -467,24 +490,24 @@ async ValueTask AssertAndLockVersion(
try
{
if (customVersionStream != null)
- logger.LogInformation("Installing custom BYOND version as {version}...", version);
+ logger.LogInformation("Installing custom BYOND version as {version}...", byondVersion);
else if (neededForLock)
{
- if (version.Build > 0)
+ if (byondEngine && byondVersion.Version.Build > 0)
throw new JobException(ErrorCode.ByondNonExistentCustomVersion);
- logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to install it.", version);
+ logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to install it.", byondVersion);
}
else
- logger.LogDebug("Requested BYOND version {version} not currently installed. Doing so now...", version);
+ logger.LogDebug("Requested BYOND version {version} not currently installed. Doing so now...", byondVersion);
if (progressReporter != null)
progressReporter.StageName = "Running event";
- var versionString = version.ToString();
+ var versionString = byondVersion.ToString();
await eventConsumer.HandleEvent(EventType.ByondInstallStart, new List { versionString }, false, cancellationToken);
- await InstallVersionFiles(progressReporter, version, customVersionStream, cancellationToken);
+ await InstallVersionFiles(progressReporter, byondVersion, customVersionStream, cancellationToken);
ourTcs.SetResult();
}
@@ -494,7 +517,7 @@ async ValueTask AssertAndLockVersion(
await eventConsumer.HandleEvent(EventType.ByondInstallFail, new List { ex.Message }, false, cancellationToken);
lock (installedVersions)
- installedVersions.Remove(version);
+ installedVersions.Remove(byondVersion);
ourTcs.SetException(ex);
throw;
@@ -513,11 +536,11 @@ async ValueTask AssertAndLockVersion(
/// Installs the files for a given BYOND .
///
/// The optional for the operation.
- /// The BYOND being installed with the number set if appropriate.
+ /// The being installed with the number set if appropriate.
/// Custom zip file to use. Will cause a number to be added.
/// The for the operation.
/// A representing the running operation.
- async ValueTask InstallVersionFiles(JobProgressReporter progressReporter, Version version, Stream customVersionStream, CancellationToken cancellationToken)
+ async ValueTask InstallVersionFiles(JobProgressReporter progressReporter, ByondVersion version, Stream customVersionStream, CancellationToken cancellationToken)
{
var installFullPath = ioManager.ResolvePath(version.ToString());
async ValueTask DirectoryCleanup()
@@ -590,27 +613,36 @@ await ioManager.WriteAllBytes(
/// The being added.
/// The representing the installation process.
/// The new containing the new .
- ReferenceCountingContainer AddInstallationContainer(Version version, Task installationTask)
+ ReferenceCountingContainer AddInstallationContainer(ByondVersion version, Task installationTask)
{
var binPathForVersion = ioManager.ConcatPath(version.ToString(), BinPath);
- var installation = new ByondInstallation(
- installationTask,
- version,
- ioManager.ResolvePath(
- ioManager.ConcatPath(
- binPathForVersion,
- byondInstaller.GetDreamDaemonName(
- version,
- out var supportsCli,
- out var supportsMapThreads))),
- ioManager.ResolvePath(
- ioManager.ConcatPath(
- binPathForVersion,
- byondInstaller.DreamMakerName)),
- supportsCli,
- supportsMapThreads);
-
- var installationContainer = new ReferenceCountingContainer(installation);
+ IByondInstallation installation;
+
+ switch (version.Engine.Value)
+ {
+ case EngineType.Byond:
+ installation = new ByondInstallation(
+ installationTask,
+ version.Version,
+ ioManager.ResolvePath(
+ ioManager.ConcatPath(
+ binPathForVersion,
+ byondInstaller.GetDreamDaemonName(
+ version.Version,
+ out var supportsCli))),
+ ioManager.ResolvePath(
+ ioManager.ConcatPath(
+ binPathForVersion,
+ byondInstaller.CompilerName)),
+ supportsCli);
+ break;
+ case EngineType.OpenDream:
+ throw new NotImplementedException();
+ default:
+ throw new InvalidOperationException($"Invalid EngineType: {version.Engine.Value}");
+ }
+
+ var installationContainer = new ReferenceCountingContainer(installation);
lock (installedVersions)
installedVersions.Add(version, installationContainer);
@@ -626,7 +658,7 @@ ReferenceCountingContainer AddInstallati
/// A representing the running operation.
async ValueTask TrustDmbPath(string fullDmbPath, CancellationToken cancellationToken)
{
- var byondDir = byondInstaller.PathToUserByondFolder;
+ var byondDir = byondInstaller.PathToUserFolder;
if (String.IsNullOrWhiteSpace(byondDir))
{
logger.LogTrace("No relevant user BYOND directory to install a \"{fileName}\" in", TrustedDmbFileName);
diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondInstallation.cs b/src/Tgstation.Server.Host/Components/Byond/IByondInstallation.cs
index 52a4e694b71..0a08217ba51 100644
--- a/src/Tgstation.Server.Host/Components/Byond/IByondInstallation.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/IByondInstallation.cs
@@ -1,4 +1,8 @@
-using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Tgstation.Server.Api.Models.Internal;
+using Tgstation.Server.Host.Components.Deployment;
namespace Tgstation.Server.Host.Components.Byond
{
@@ -8,28 +12,47 @@ namespace Tgstation.Server.Host.Components.Byond
public interface IByondInstallation
{
///
- /// The of the .
+ /// The of the .
///
- Version Version { get; }
+ ByondVersion Version { get; }
///
- /// The full path to the DreamDaemon executable.
+ /// The full path to the game server executable.
///
- string DreamDaemonPath { get; }
+ string ServerExePath { get; }
///
/// The full path to the dm/DreamMaker executable.
///
- string DreamMakerPath { get; }
+ string CompilerExePath { get; }
+
+ ///
+ /// If supports being run as a command-line application and outputs log information to be captured.
+ ///
+ bool HasStandardOutput { get; }
+
+ ///
+ /// If may create network prompts.
+ ///
+ bool PromptsForNetworkAccess { get; }
///
- /// If supports being run as a command-line application.
+ /// The that completes when the BYOND version finished installing.
///
- bool SupportsCli { get; }
+ Task InstallationTask { get; }
///
- /// If supports the -map-threads parameter.
+ /// Return the command line arguments for launching with given .
///
- bool SupportsMapThreads { get; }
+ /// The .
+ /// The map of parameter s as a . Should NOT include the of .
+ /// The .
+ /// The path to the log file, if any.
+ /// The formatted arguments .
+ string FormatServerArguments(
+ IDmbProvider dmbProvider,
+ IReadOnlyDictionary parameters,
+ DreamDaemonLaunchParameters launchParameters,
+ string logFilePath);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs
index 9c353378303..b977c31353c 100644
--- a/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs
@@ -1,8 +1,9 @@
-using System;
-using System.IO;
+using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using Tgstation.Server.Api.Models.Internal;
+
namespace Tgstation.Server.Host.Components.Byond
{
///
@@ -11,52 +12,61 @@ namespace Tgstation.Server.Host.Components.Byond
interface IByondInstaller
{
///
- /// Get the file name of the DreamMaker executable.
+ /// Get the file name of the compiler executable.
///
- string DreamMakerName { get; }
+ string CompilerName { get; }
///
- /// The path to the BYOND folder for the user.
+ /// The path to the folder for the user's data.
///
- string PathToUserByondFolder { get; }
+ string PathToUserFolder { get; }
///
/// Get the file name of the DreamDaemon executable.
///
- /// The of BYOND to select the executable name for.
+ /// The of BYOND to select the executable name for.
/// Whether or not the returned path supports being run as a command-line application.
/// Whether or not the returned path supports the '-map-threads' parameter.
/// The file name of the DreamDaemon executable.
- string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads);
+ string GetDreamDaemonName(ByondVersion version, out bool supportsCli, out bool supportsMapThreads);
///
/// Download a given BYOND .
///
- /// The of BYOND to download.
+ /// The of BYOND to download.
/// The for the operation.
/// A resulting in a of the zipfile.
- ValueTask DownloadVersion(Version version, CancellationToken cancellationToken);
+ ValueTask DownloadVersion(ByondVersion version, CancellationToken cancellationToken);
///
/// Does actions necessary to get an extracted BYOND installation working.
///
- /// The of BYOND being installed.
+ /// The being installed.
+ /// The path to the BYOND installation.
+ /// The for the operation.
+ /// A representing the running operation.
+ ValueTask InstallByond(ByondVersion version, string path, CancellationToken cancellationToken);
+
+ ///
+ /// Does actions necessary to remove a given
+ ///
+ /// The being installed.
/// The path to the BYOND installation.
/// The for the operation.
/// A representing the running operation.
- ValueTask InstallByond(Version version, string path, CancellationToken cancellationToken);
+ ValueTask DeleteVersion(ByondVersion version, string path, CancellationToken cancellationToken);
///
/// Does actions necessary to get upgrade a BYOND version installed by a previous version of TGS.
///
- /// The of BYOND being installed.
+ /// The being installed.
/// The path to the BYOND installation.
/// The for the operation.
/// A representing the running operation.
- ValueTask UpgradeInstallation(Version version, string path, CancellationToken cancellationToken);
+ ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken);
///
- /// Attempts to cleans the BYOND cache folder for the system.
+ /// Attempts to cleans the engine's cache folder for the system.
///
/// The for the operation.
/// A representing the running operation.
diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs b/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs
index 092034b1c0b..a8a0c8d0d9b 100644
--- a/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs
@@ -4,6 +4,8 @@
using System.Threading;
using System.Threading.Tasks;
+using Tgstation.Server.Api.Models;
+using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Jobs;
namespace Tgstation.Server.Host.Components.Byond
@@ -11,48 +13,62 @@ namespace Tgstation.Server.Host.Components.Byond
///
/// For managing the BYOND installation.
///
- /// When passing in s, ensure they are BYOND format versions unless referring to a custom version. This means should NEVER be 0.
+ /// When passing in s, ensure they are BYOND format versions unless referring to a custom or version. This means should NEVER be 0.
public interface IByondManager : IComponentService, IDisposable
{
///
- /// The currently active BYOND version.
+ /// The currently active .
///
- Version ActiveVersion { get; }
+ ByondVersion ActiveVersion { get; }
///
- /// The installed BYOND versions.
+ /// The installed s.
///
- IReadOnlyList InstalledVersions { get; }
+ IReadOnlyList InstalledVersions { get; }
///
- /// Change the active BYOND version.
+ /// Ensure that the given is registered for the given .
+ ///
+ /// The source of the .
+ /// The .
+ /// The for the operation.
+ /// A representing the running operation.
+ ValueTask EnsureEngineSource(Uri source, EngineType engine, CancellationToken cancellationToken);
+
+ ///
+ /// Change the active .
///
/// The optional for the operation.
- /// The new .
+ /// The new .
/// Optional of a custom BYOND version zip file.
/// If an installation should be performed if the is not installed. If and an installation is required an will be thrown.
/// The for the operation.
/// A representing the running operation.
- ValueTask ChangeVersion(JobProgressReporter progressReporter, Version version, Stream customVersionStream, bool allowInstallation, CancellationToken cancellationToken);
+ ValueTask ChangeVersion(
+ JobProgressReporter progressReporter,
+ ByondVersion version,
+ Stream customVersionStream,
+ bool allowInstallation,
+ CancellationToken cancellationToken);
///
- /// Deletes a given BYOND version from the disk.
+ /// Deletes a given from the disk.
///
/// The for the operation.
- /// The to delete.
+ /// The to delete.
/// The for the operation.
- /// A representing the running operation.
- ValueTask DeleteVersion(JobProgressReporter progressReporter, Version version, CancellationToken cancellationToken);
+ /// A representing the running operation.
+ ValueTask DeleteVersion(JobProgressReporter progressReporter, ByondVersion version, CancellationToken cancellationToken);
///
/// Lock the current installation's location and return a .
///
- /// The BYOND required.
+ /// The required.
/// The optional full path to .dmb to trust while using the executables.
/// The for the operation.
/// A resulting in the requested .
ValueTask UseExecutables(
- Version requiredVersion,
+ ByondVersion requiredVersion,
string trustDmbFullPath,
CancellationToken cancellationToken);
}
diff --git a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs
index 3657e015d47..ee71983054d 100644
--- a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs
@@ -32,10 +32,10 @@ sealed class PosixByondInstaller : ByondInstallerBase
const string ShellScriptExtension = ".sh";
///
- public override string DreamMakerName => DreamMakerExecutableName + ShellScriptExtension;
+ public override string CompilerName => DreamMakerExecutableName + ShellScriptExtension;
///
- public override string PathToUserByondFolder { get; }
+ public override string PathToUserFolder { get; }
///
protected override string ByondRevisionsUrlTemplate => "https://www.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip";
@@ -61,7 +61,7 @@ public PosixByondInstaller(
{
this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
- PathToUserByondFolder = IOManager.ResolvePath(
+ PathToUserFolder = IOManager.ResolvePath(
IOManager.ConcatPath(
Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile),
@@ -69,12 +69,11 @@ public PosixByondInstaller(
}
///
- public override string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads)
+ public override string GetDreamDaemonName(Version version, out bool supportsCli)
{
ArgumentNullException.ThrowIfNull(version);
supportsCli = true;
- supportsMapThreads = version >= MapThreadsVersion;
return DreamDaemonExecutableName + ShellScriptExtension;
}
@@ -105,7 +104,7 @@ async ValueTask WriteAndMakeExecutable(string pathToScript, string script)
dreamDaemonScript);
var dmTask = WriteAndMakeExecutable(
- IOManager.ConcatPath(basePath, DreamMakerName),
+ IOManager.ConcatPath(basePath, CompilerName),
dreamMakerScript);
var task = ValueTaskExtensions.WhenAll(
diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs
index 8080abc12ff..15a22e84eea 100644
--- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs
+++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs
@@ -53,10 +53,10 @@ sealed class WindowsByondInstaller : ByondInstallerBase, IDisposable
public static Version DDExeVersion => new (515, 1598);
///
- public override string DreamMakerName => "dm.exe";
+ public override string CompilerName => "dm.exe";
///
- public override string PathToUserByondFolder { get; }
+ public override string PathToUserFolder { get; }
///
protected override string ByondRevisionsUrlTemplate => "https://www.byond.com/download/build/{0}/{0}.{1}_byond.zip";
@@ -102,9 +102,9 @@ public WindowsByondInstaller(
var documentsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (String.IsNullOrWhiteSpace(documentsDirectory))
- PathToUserByondFolder = null; // happens with the service account
+ PathToUserFolder = null; // happens with the service account
else
- PathToUserByondFolder = IOManager.ResolvePath(IOManager.ConcatPath(documentsDirectory, "BYOND"));
+ PathToUserFolder = IOManager.ResolvePath(IOManager.ConcatPath(documentsDirectory, "BYOND"));
semaphore = new SemaphoreSlim(1);
installedDirectX = false;
@@ -114,12 +114,11 @@ public WindowsByondInstaller(
public void Dispose() => semaphore.Dispose();
///
- public override string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads)
+ public override string GetDreamDaemonName(Version version, out bool supportsCli)
{
ArgumentNullException.ThrowIfNull(version);
supportsCli = version >= DDExeVersion;
- supportsMapThreads = version >= MapThreadsVersion;
return supportsCli ? "dd.exe" : "dreamdaemon.exe";
}
diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
index 30f6a555df5..1571978b299 100644
--- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
@@ -637,7 +637,7 @@ await eventConsumer.HandleEvent(
// run compiler
progressReporter.StageName = "Running DreamMaker";
- var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken);
+ var exitCode = await RunDreamMaker(byondLock.CompilerExePath, job, cancellationToken);
// Session takes ownership of the lock and Disposes it so save this for later
var byondVersion = byondLock.Version;
diff --git a/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs
index efc773a9193..8f81cb7fbc1 100644
--- a/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs
+++ b/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs
@@ -1,6 +1,6 @@
using System;
-using Tgstation.Server.Host.Models;
+using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Host.Components.Deployment
{
@@ -22,7 +22,12 @@ public interface IDmbProvider : IDisposable
///
/// The of the .dmb.
///
- CompileJob CompileJob { get; }
+ Models.CompileJob CompileJob { get; }
+
+ ///
+ /// The used to build the .dmb.
+ ///
+ ByondVersion ByondVersion { get; }
///
/// Disposing the won't cause a cleanup of the working directory.
diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs
index 3bc79939c7d..92954b7b2e0 100644
--- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs
+++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs
@@ -128,38 +128,6 @@ sealed class SessionControllerFactory : ISessionControllerFactory
///
readonly Api.Models.Instance instance;
- ///
- /// Change a given into the appropriate DreamDaemon command line word.
- ///
- /// The level to change.
- /// A representation of the command line parameter.
- static string SecurityWord(DreamDaemonSecurity securityLevel)
- {
- return securityLevel switch
- {
- DreamDaemonSecurity.Safe => "safe",
- DreamDaemonSecurity.Trusted => "trusted",
- DreamDaemonSecurity.Ultrasafe => "ultrasafe",
- _ => throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel)),
- };
- }
-
- ///
- /// Change a given into the appropriate DreamDaemon command line word.
- ///
- /// The level to change.
- /// A representation of the command line parameter.
- static string VisibilityWord(DreamDaemonVisibility visibility)
- {
- return visibility switch
- {
- DreamDaemonVisibility.Public => "public",
- DreamDaemonVisibility.Private => "private",
- DreamDaemonVisibility.Invisible => "invisible",
- _ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon visibility level: {0}", visibility)),
- };
- }
-
///
/// Check if a given can be bound to.
///
@@ -274,7 +242,7 @@ public async ValueTask LaunchNew(
// get the byond lock
var byondLock = currentByondLock ?? await byond.UseExecutables(
- Version.Parse(dmbProvider.CompileJob.ByondVersion),
+ dmbProvider.ByondVersion,
gameIOManager.ConcatPath(dmbProvider.Directory, dmbProvider.DmbName),
cancellationToken);
try
@@ -289,7 +257,7 @@ public async ValueTask LaunchNew(
string outputFilePath = null;
var preserveLogFile = true;
- var cliSupported = byondLock.SupportsCli;
+ var hasStandardOutput = byondLock.HasStandardOutput;
if (launchParameters.LogOutput.Value)
{
var now = DateTimeOffset.UtcNow;
@@ -302,7 +270,7 @@ public async ValueTask LaunchNew(
logger.LogInformation("Logging DreamDaemon output to {path}...", outputFilePath);
}
- else if (!cliSupported)
+ else if (!hasStandardOutput)
{
outputFilePath = gameIOManager.ConcatPath(dmbProvider.Directory, $"{Guid.NewGuid()}.dd.log");
preserveLogFile = false;
@@ -310,17 +278,12 @@ public async ValueTask LaunchNew(
var accessIdentifier = cryptographySuite.GetSecureString();
- var byondTopicSender = topicClientFactory.CreateTopicClient(
- TimeSpan.FromMilliseconds(
- launchParameters.TopicRequestTimeout.Value));
-
if (!apiValidate && dmbProvider.CompileJob.DMApiVersion == null)
logger.LogDebug("Session will have no DMAPI support!");
// launch dd
- var process = await CreateDreamDaemonProcess(
+ var process = await CreateGameServerProcess(
dmbProvider,
- byondTopicSender,
byondLock,
launchParameters,
accessIdentifier,
@@ -347,6 +310,10 @@ public async ValueTask LaunchNew(
accessIdentifier,
launchParameters.Port.Value);
+ var byondTopicSender = topicClientFactory.CreateTopicClient(
+ TimeSpan.FromMilliseconds(
+ launchParameters.TopicRequestTimeout.Value));
+
var sessionController = new SessionController(
reattachInformation,
instance,
@@ -362,7 +329,7 @@ public async ValueTask LaunchNew(
() => LogDDOutput(
process,
outputFilePath,
- cliSupported,
+ hasStandardOutput,
preserveLogFile,
CancellationToken.None), // DCT: None available
launchParameters.StartupTimeout,
@@ -406,7 +373,7 @@ public async ValueTask Reattach(
logger.LogTrace("Begin session reattach...");
var byondTopicSender = topicClientFactory.CreateTopicClient(reattachInformation.TopicRequestTimeout);
var byondLock = await byond.UseExecutables(
- Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion),
+ reattachInformation.Dmb.ByondVersion,
null, // Doesn't matter if it's trusted or not on reattach
cancellationToken);
@@ -423,7 +390,7 @@ public async ValueTask Reattach(
try
{
- if (!byondLock.SupportsCli)
+ if (byondLock.PromptsForNetworkAccess)
networkPromptReaper.RegisterProcess(process);
var chatTrackingContext = chat.CreateTrackingContext();
@@ -479,10 +446,9 @@ public async ValueTask Reattach(
}
///
- /// Creates the DreamDaemon .
+ /// Creates the game server .
///
/// The .
- /// The to use for sanitization.
/// The .
/// The .
/// The secure string to use for the session.
@@ -490,9 +456,8 @@ public async ValueTask Reattach(
/// If we are only validating the DMAPI then exiting.
/// The for the operation.
/// A resulting in the DreamDaemon .
- async ValueTask CreateDreamDaemonProcess(
+ async ValueTask CreateGameServerProcess(
IDmbProvider dmbProvider,
- ITopicClient byondTopicSender,
IByondExecutableLock byondLock,
DreamDaemonLaunchParameters launchParameters,
string accessIdentifier,
@@ -500,39 +465,26 @@ async ValueTask CreateDreamDaemonProcess(
bool apiValidate,
CancellationToken cancellationToken)
{
- // set command line options
- // more sanitization here cause it uses the same scheme
- var parameters = $"{DMApiConstants.ParamApiVersion}={byondTopicSender.SanitizeString(DMApiConstants.InteropVersion.Semver().ToString())}&{byondTopicSender.SanitizeString(DMApiConstants.ParamServerPort)}={serverPortProvider.HttpApiPort}&{byondTopicSender.SanitizeString(DMApiConstants.ParamAccessIdentifier)}={byondTopicSender.SanitizeString(accessIdentifier)}";
-
- if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
- parameters = $"{parameters}&{launchParameters.AdditionalParameters}";
-
// important to run on all ports to allow port changing
- var arguments = String.Format(
- CultureInfo.InvariantCulture,
- "{0} -port {1} -ports 1-65535 {2}-close -verbose -{3} -{4}{5}{6}{7} -params \"{8}\"",
- dmbProvider.DmbName,
- launchParameters.Port.Value,
- launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
- SecurityWord(launchParameters.SecurityLevel.Value),
- VisibilityWord(launchParameters.Visibility.Value),
- !byondLock.SupportsCli
- ? $" -logself -log {logFilePath}"
- : String.Empty, // DD doesn't output anything if -logself is set???
- launchParameters.StartProfiler.Value
- ? " -profile"
- : String.Empty,
- byondLock.SupportsMapThreads && launchParameters.MapThreads.Value != 0
- ? $" -map-threads {launchParameters.MapThreads.Value}"
- : String.Empty,
- parameters);
+ var arguments = byondLock.FormatServerArguments(
+ dmbProvider,
+ new Dictionary
+ {
+ { DMApiConstants.ParamApiVersion, DMApiConstants.InteropVersion.Semver().ToString() },
+ { DMApiConstants.ParamServerPort, serverPortProvider.HttpApiPort.ToString(CultureInfo.InvariantCulture) },
+ { DMApiConstants.ParamAccessIdentifier, accessIdentifier },
+ },
+ launchParameters,
+ !byondLock.HasStandardOutput
+ ? logFilePath
+ : null);
var process = processExecutor.LaunchProcess(
- byondLock.DreamDaemonPath,
+ byondLock.ServerExePath,
dmbProvider.Directory,
arguments,
logFilePath,
- byondLock.SupportsCli,
+ byondLock.HasStandardOutput,
true);
try
@@ -545,7 +497,7 @@ async ValueTask CreateDreamDaemonProcess(
else if (sessionConfiguration.LowPriorityDeploymentProcesses)
process.AdjustPriority(false);
- if (!byondLock.SupportsCli)
+ if (!byondLock.HasStandardOutput)
networkPromptReaper.RegisterProcess(process);
// If this isnt a staging DD (From a Deployment), fire off an event
diff --git a/src/Tgstation.Server.Host/Controllers/ByondController.cs b/src/Tgstation.Server.Host/Controllers/ByondController.cs
index 156ff784e93..082ab8b573b 100644
--- a/src/Tgstation.Server.Host/Controllers/ByondController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ByondController.cs
@@ -43,7 +43,7 @@ public sealed class ByondController : InstanceRequiredController
///
/// The to normalize.
/// The normalized . May be a reference to .
- static Version NormalizeVersion(Version version) => version.Build == 0 ? new Version(version.Major, version.Minor) : version;
+ static Version NormalizeByondVersion(Version version) => version.Build == 0 ? new Version(version.Major, version.Minor) : version;
///
/// Initializes a new instance of the class.
@@ -72,7 +72,7 @@ public ByondController(
}
///
- /// Gets the active .
+ /// Gets the active .
///
/// A resulting in the for the operation.
/// Retrieved version information successfully.
@@ -82,13 +82,12 @@ public ByondController(
public ValueTask Read()
=> WithComponentInstance(instance =>
ValueTask.FromResult(
- Json(new ByondResponse
- {
- Version = instance.ByondManager.ActiveVersion,
- })));
+ Json(
+ new ByondResponse(
+ instance.ByondManager.ActiveVersion))));
///
- /// Lists installed s.
+ /// Lists installed s.
///
/// The current page.
/// The page size.
@@ -106,10 +105,7 @@ public ValueTask List([FromQuery] int? page, [FromQuery] int? pag
instance
.ByondManager
.InstalledVersions
- .Select(x => new ByondResponse
- {
- Version = x,
- })
+ .Select(x => new ByondResponse(x))
.AsQueryable()
.OrderBy(x => x.Version))),
null,
@@ -126,7 +122,11 @@ public ValueTask List([FromQuery] int? page, [FromQuery] int? pag
/// Switched active version successfully.
/// Created to install and switch active version successfully.
[HttpPost]
- [TgsAuthorize(ByondRights.InstallOfficialOrChangeActiveVersion | ByondRights.InstallCustomVersion)]
+ [TgsAuthorize(
+ ByondRights.InstallOfficialOrChangeActiveByondVersion
+ | ByondRights.InstallCustomByondVersion
+ | ByondRights.InstallOfficialOrChangeActiveOpenDreamVersion
+ | ByondRights.InstallCustomOpenDreamVersion)]
[ProducesResponseType(typeof(ByondInstallResponse), 200)]
[ProducesResponseType(typeof(ByondInstallResponse), 202)]
#pragma warning disable CA1506 // TODO: Decomplexify
@@ -136,17 +136,18 @@ public async ValueTask Update([FromBody] ByondVersionRequest mode
ArgumentNullException.ThrowIfNull(model);
var uploadingZip = model.UploadCustomZip == true;
+ var isByondEngine = model.Engine.Value == EngineType.Byond;
- if (model.Version == null
- || model.Version.Revision != -1
- || (uploadingZip && model.Version.Build > 0))
+ if (model.Version.Revision != -1
+ || (isByondEngine && ((uploadingZip && model.Version.Build > 0) || model.SourceCommittish != null || model.SourceRepository != null)))
return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
- var version = NormalizeVersion(model.Version);
+ if (model.Engine == EngineType.Byond)
+ model.Version = NormalizeByondVersion(model.Version);
var userByondRights = AuthenticationContext.InstancePermissionSet.ByondRights.Value;
- if ((!userByondRights.HasFlag(ByondRights.InstallOfficialOrChangeActiveVersion) && !uploadingZip)
- || (!userByondRights.HasFlag(ByondRights.InstallCustomVersion) && uploadingZip))
+ if ((!userByondRights.HasFlag(ByondRights.InstallOfficialOrChangeActiveByondVersion) && !uploadingZip)
+ || (!userByondRights.HasFlag(ByondRights.InstallCustomByondVersion) && uploadingZip))
return Forbid();
// remove cruff fields
@@ -155,44 +156,50 @@ public async ValueTask Update([FromBody] ByondVersionRequest mode
async instance =>
{
var byondManager = instance.ByondManager;
- var versionAlreadyInstalled = !uploadingZip && byondManager.InstalledVersions.Any(x => x == version);
+ var versionAlreadyInstalled = !uploadingZip && byondManager.InstalledVersions.Any(x => x.Equals(model));
if (versionAlreadyInstalled)
{
Logger.LogInformation(
- "User ID {userId} changing instance ID {instanceId} BYOND version to {newByondVersion}",
+ "User ID {userId} changing instance ID {instanceId} {engineType} version to {newByondVersion}",
AuthenticationContext.User.Id,
Instance.Id,
- version);
+ model.Engine,
+ model.Version);
try
{
- await byondManager.ChangeVersion(null, version, null, false, cancellationToken);
+ await byondManager.ChangeVersion(null, model, null, false, cancellationToken);
}
catch (InvalidOperationException ex)
{
Logger.LogDebug(
ex,
- "Race condition: BYOND version {version} uninstalled before we could switch to it. Creating install job instead...",
- version);
+ "Race condition: {engineType} version {version} uninstalled before we could switch to it. Creating install job instead...",
+ model.Engine.Value,
+ model.Version);
versionAlreadyInstalled = false;
}
}
if (!versionAlreadyInstalled)
{
- if (version.Build > 0)
+ if (model.Version.Build > 0)
return BadRequest(new ErrorMessageResponse(ErrorCode.ByondNonExistentCustomVersion));
Logger.LogInformation(
- "User ID {userId} installing BYOND version to {newByondVersion} on instance ID {instanceId}",
+ "User ID {userId} installing {engineType} version {newByondVersion}{sourceCommittish} on instance ID {instanceId}",
AuthenticationContext.User.Id,
- version,
+ model.Engine.Value,
+ model.Version,
+ model.SourceCommittish != null
+ ? $" ({model.SourceCommittish})"
+ : String.Empty,
Instance.Id);
// run the install through the job manager
var job = new Job
{
- Description = $"Install {(!uploadingZip ? String.Empty : "custom ")}BYOND version {version}",
+ Description = $"Install {(!uploadingZip ? String.Empty : "custom ")}{model.Engine.Value} version {model.Version}",
StartedBy = AuthenticationContext.User,
CancelRightsType = RightsType.Byond,
CancelRight = (ulong)ByondRights.CancelInstall,
@@ -209,7 +216,13 @@ await jobManager.RegisterOperation(
job,
async (core, databaseContextFactory, paramJob, progressHandler, jobCancellationToken) =>
{
- Stream zipFileStream = null;
+ if (model.SourceRepository != null)
+ await core.ByondManager.EnsureEngineSource(
+ model.SourceRepository,
+ model.Engine.Value,
+ jobCancellationToken);
+
+ MemoryStream zipFileStream = null;
if (fileUploadTicket != null)
await using (fileUploadTicket)
{
@@ -229,7 +242,7 @@ await jobManager.RegisterOperation(
await using (zipFileStream)
await core.ByondManager.ChangeVersion(
progressHandler,
- version,
+ model,
zipFileStream,
true,
jobCancellationToken);
@@ -260,7 +273,7 @@ await core.ByondManager.ChangeVersion(
/// A resulting in the for the operation.
/// Created to delete target version successfully.
/// Attempted to delete the active BYOND .
- /// The specified was not installed.
+ /// The specified was not installed.
[HttpDelete]
[TgsAuthorize(ByondRights.DeleteInstall)]
[ProducesResponseType(typeof(JobResponse), 202)]
@@ -270,22 +283,22 @@ public async ValueTask Delete([FromBody] ByondVersionDeleteReques
{
ArgumentNullException.ThrowIfNull(model);
- if (model.Version == null
- || model.Version.Revision != -1)
+ if (model.Version.Revision != -1)
return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
- var version = NormalizeVersion(model.Version);
+ if (model.Engine == EngineType.Byond)
+ model.Version = NormalizeByondVersion(model.Version);
var notInstalledResponse = await WithComponentInstance(
instance =>
{
var byondManager = instance.ByondManager;
- if (version == byondManager.ActiveVersion)
+ if (model.Equals(byondManager.ActiveVersion))
return ValueTask.FromResult(
Conflict(new ErrorMessageResponse(ErrorCode.ByondCannotDeleteActiveVersion)));
- var versionNotInstalled = !byondManager.InstalledVersions.Any(x => x == version);
+ var versionNotInstalled = !byondManager.InstalledVersions.Any(x => x.Equals(model));
return ValueTask.FromResult(
versionNotInstalled
@@ -296,22 +309,27 @@ public async ValueTask Delete([FromBody] ByondVersionDeleteReques
if (notInstalledResponse != null)
return notInstalledResponse;
- var isCustomVersion = version.Build != -1;
+ var isByondVersion = model.Engine.Value == EngineType.Byond;
// run the install through the job manager
var job = new Job
{
- Description = $"Delete installed BYOND version {version}",
+ Description = $"Delete installed {model.Engine.Value} version {model.Version}",
StartedBy = AuthenticationContext.User,
CancelRightsType = RightsType.Byond,
- CancelRight = (ulong)(isCustomVersion ? ByondRights.InstallOfficialOrChangeActiveVersion : ByondRights.InstallCustomVersion),
+ CancelRight = (ulong)(
+ isByondVersion
+ ? model.Version.Build != -1
+ ? ByondRights.InstallOfficialOrChangeActiveByondVersion
+ : ByondRights.InstallCustomByondVersion
+ : ByondRights.InstallCustomOpenDreamVersion | ByondRights.InstallOfficialOrChangeActiveOpenDreamVersion),
Instance = Instance,
};
await jobManager.RegisterOperation(
job,
(instanceCore, databaseContextFactory, job, progressReporter, jobCancellationToken)
- => instanceCore.ByondManager.DeleteVersion(progressReporter, version, jobCancellationToken),
+ => instanceCore.ByondManager.DeleteVersion(progressReporter, model, jobCancellationToken),
cancellationToken);
var apiResponse = job.ToApi();
diff --git a/src/Tgstation.Server.Host/Models/CompileJob.cs b/src/Tgstation.Server.Host/Models/CompileJob.cs
index 9ddde1344c1..92f45ab843e 100644
--- a/src/Tgstation.Server.Host/Models/CompileJob.cs
+++ b/src/Tgstation.Server.Host/Models/CompileJob.cs
@@ -32,6 +32,12 @@ public sealed class CompileJob : Api.Models.Internal.CompileJob, IApiTransformab
[Required]
public string ByondVersion { get; set; }
+ ///
+ /// The of .
+ ///
+ [Required]
+ public EngineType Engine { get; set; }
+
///
/// Backing field for of .
///
@@ -82,7 +88,7 @@ public override Version DMApiVersion
}
///
- public CompileJobResponse ToApi() => new CompileJobResponse
+ public CompileJobResponse ToApi() => new ()
{
DirectoryName = DirectoryName,
DmeName = DmeName,
diff --git a/tests/Tgstation.Server.Api.Tests/Rights/TestRights.cs b/tests/Tgstation.Server.Api.Tests/Rights/TestRights.cs
index 62e41b7d982..e773f4da941 100644
--- a/tests/Tgstation.Server.Api.Tests/Rights/TestRights.cs
+++ b/tests/Tgstation.Server.Api.Tests/Rights/TestRights.cs
@@ -40,7 +40,7 @@ public void TestAllPowerOfTwo()
[TestMethod]
public void TestAllRightsWorks()
{
- var allByondRights = ByondRights.CancelInstall | ByondRights.InstallOfficialOrChangeActiveVersion | ByondRights.ListInstalled | ByondRights.ReadActive | ByondRights.InstallCustomVersion | ByondRights.DeleteInstall;
+ var allByondRights = ByondRights.CancelInstall | ByondRights.InstallOfficialOrChangeActiveByondVersion | ByondRights.ListInstalled | ByondRights.ReadActive | ByondRights.InstallCustomByondVersion | ByondRights.DeleteInstall;
var automaticByondRights = RightsHelper.AllRights();
Assert.AreEqual(allByondRights, automaticByondRights);
diff --git a/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs
index eedac0cd0e5..0d318b15b42 100644
--- a/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs
+++ b/tests/Tgstation.Server.Tests/Live/Instance/ByondTest.cs
@@ -58,7 +58,7 @@ public static async Task GetEdgeVersion(IFileDownloader fileDownloader,
await using var provider = fileDownloader.DownloadFile(new Uri("https://www.byond.com/download/version.txt"), null);
var stream = await provider.GetResult(cancellationToken);
using var reader = new StreamReader(stream, Encoding.UTF8, false, -1, true);
- var text = await reader.ReadToEndAsync();
+ var text = await reader.ReadToEndAsync(cancellationToken);
var splits = text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
var targetVersion = splits.Last();
@@ -83,9 +83,21 @@ async Task RunPartOne(CancellationToken cancellationToken)
{
testVersion = await GetEdgeVersion(fileDownloader, cancellationToken);
await TestNoVersion(cancellationToken);
+ await TestInstallNullVersion(cancellationToken);
await TestInstallStable(cancellationToken);
}
+ Task TestInstallNullVersion(CancellationToken cancellationToken)
+ => ApiAssert.ThrowsException(
+ () => byondClient.SetActiveVersion(
+ new ByondVersionRequest
+ {
+ Engine = EngineType.Byond,
+ },
+ null,
+ cancellationToken),
+ ErrorCode.ModelValidationFailure);
+
async Task RunContinued(Task firstInstall, CancellationToken cancellationToken)
{
await firstInstall;
diff --git a/tests/Tgstation.Server.Tests/TestDatabase.cs b/tests/Tgstation.Server.Tests/TestDatabase.cs
index df8ffd5e4cf..8d629bcfb67 100644
--- a/tests/Tgstation.Server.Tests/TestDatabase.cs
+++ b/tests/Tgstation.Server.Tests/TestDatabase.cs
@@ -134,7 +134,7 @@ DatabaseContext CreateContext()
{
new Host.Models.InstancePermissionSet
{
- ByondRights = ByondRights.InstallCustomVersion,
+ ByondRights = ByondRights.InstallCustomByondVersion,
ChatBotRights = ChatBotRights.None,
ConfigurationRights = ConfigurationRights.Read,
DreamDaemonRights = DreamDaemonRights.ReadRevision,