More OpenDream Integration

- Make use of `world.opendream_topic_port` in DMAPI if present. Send in initial bridge request.
- Implement compiler and server arguments for `OpenDreamInstallation`.
- Deprecate bridge port update requests.
- Prevent symlink swapping if not running a BYOND engine.
This commit is contained in:
Jordan Dominion
2023-10-14 00:21:45 -04:00
parent 10a0937945
commit d4b3583ff3
19 changed files with 205 additions and 145 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
<TgsApiLibraryVersion>12.0.0</TgsApiLibraryVersion>
<TgsClientVersion>13.0.0</TgsClientVersion>
<TgsDmapiVersion>6.6.0</TgsDmapiVersion>
<TgsInteropVersion>5.6.1</TgsInteropVersion>
<TgsInteropVersion>5.7.0</TgsInteropVersion>
<TgsHostWatchdogVersion>1.4.0</TgsHostWatchdogVersion>
<TgsContainerScriptVersion>1.2.1</TgsContainerScriptVersion>
<TgsMigratorVersion>2.0.0</TgsMigratorVersion>
+1 -1
View File
@@ -1 +1 @@
"5.6.1"
"5.7.0"
+1 -1
View File
@@ -8,7 +8,6 @@
#define DMAPI5_TOPIC_REQUEST_LIMIT 65528
#define DMAPI5_TOPIC_RESPONSE_LIMIT 65529
#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
#define DMAPI5_BRIDGE_COMMAND_PRIME 2
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
@@ -18,6 +17,7 @@
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort"
#define DMAPI5_CHUNK "chunk"
#define DMAPI5_CHUNK_PAYLOAD "payload"
+8 -1
View File
@@ -38,7 +38,7 @@
var/datum/tgs_version/api_version = ApiVersion()
version = null
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort()))
if(!istype(bridge_response))
TGS_ERROR_LOG("Failed initial bridge request!")
return FALSE
@@ -100,6 +100,13 @@
initialized = TRUE
return TRUE
/datum/tgs_api/v5/proc/GetTopicPort()
#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS)
return "[world.opendream_topic_port]"
#else
return null
#endif
/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
TGS_DEBUG_LOG("RequireInitialBridgeResponse()")
var/logged = FALSE
+1
View File
@@ -175,6 +175,7 @@
var/list/reattach_response = TopicResponse(error_message)
reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands()
reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort()
return reattach_response
if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK)
+1 -1
View File
@@ -8,7 +8,6 @@
#undef DMAPI5_TOPIC_REQUEST_LIMIT
#undef DMAPI5_TOPIC_RESPONSE_LIMIT
#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE
#undef DMAPI5_BRIDGE_COMMAND_STARTUP
#undef DMAPI5_BRIDGE_COMMAND_PRIME
#undef DMAPI5_BRIDGE_COMMAND_REBOOT
@@ -18,6 +17,7 @@
#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
#undef DMAPI5_PARAMETER_TOPIC_PORT
#undef DMAPI5_CHUNK
#undef DMAPI5_CHUNK_PAYLOAD
@@ -9,21 +9,21 @@ namespace Tgstation.Server.Api.Models.Internal
public class DreamDaemonLaunchParameters
{
/// <summary>
/// If the BYOND web client can be used to connect to the game server.
/// If the BYOND web client can be used to connect to the game server. No-op for <see cref="EngineType.OpenDream"/>.
/// </summary>
[Required]
[ResponseOptions]
public bool? AllowWebClient { get; set; }
/// <summary>
/// If -profile is passed in on the DreamDaemon command line.
/// If -profile is passed in on the DreamDaemon command line. No-op for <see cref="EngineType.OpenDream"/>.
/// </summary>
[Required]
[ResponseOptions]
public bool? StartProfiler { get; set; }
/// <summary>
/// The <see cref="DreamDaemonVisibility"/> level of DreamDaemon.
/// The <see cref="DreamDaemonVisibility"/> level of DreamDaemon. No-op for <see cref="EngineType.OpenDream"/>.
/// </summary>
[Required]
[ResponseOptions]
@@ -31,7 +31,7 @@ namespace Tgstation.Server.Api.Models.Internal
public DreamDaemonVisibility? Visibility { get; set; }
/// <summary>
/// The <see cref="DreamDaemonSecurity"/> level of DreamDaemon.
/// The <see cref="DreamDaemonSecurity"/> level of DreamDaemon. No-op for <see cref="EngineType.OpenDream"/>.
/// </summary>
[Required]
[ResponseOptions]
@@ -92,7 +92,7 @@ namespace Tgstation.Server.Api.Models.Internal
public bool? LogOutput { get; set; }
/// <summary>
/// If DreamDaemon supports it, the value added as the -map-threads parameter. 0 uses the default BYOND value.
/// If DreamDaemon supports it, the value added as the -map-threads parameter. 0 uses the default BYOND value. No-op for <see cref="EngineType.OpenDream"/>.
/// </summary>
[Required]
[ResponseOptions]
@@ -6,19 +6,16 @@ using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components.Deployment
{
/// <inheritdoc />
sealed class DmbProvider : IDmbProvider
sealed class DmbProvider : DmbProviderBase, IDmbProvider
{
/// <inheritdoc />
public string DmbName => String.Concat(CompileJob.DmeName, DreamMaker.DmbExtension);
public override string Directory => ioManager.ResolvePath(CompileJob.DirectoryName.ToString() + directoryAppend);
/// <inheritdoc />
public string Directory => ioManager.ResolvePath(CompileJob.DirectoryName.ToString() + directoryAppend);
public override Models.CompileJob CompileJob { get; }
/// <inheritdoc />
public Models.CompileJob CompileJob { get; }
/// <inheritdoc />
public ByondVersion ByondVersion { get; }
public override ByondVersion ByondVersion { get; }
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="DmbProvider"/>.
@@ -53,9 +50,9 @@ namespace Tgstation.Server.Host.Components.Deployment
}
/// <inheritdoc />
public void Dispose() => onDispose?.Invoke();
public override void Dispose() => onDispose?.Invoke();
/// <inheritdoc />
public void KeepAlive() => onDispose = null;
public override void KeepAlive() => onDispose = null;
}
}
@@ -0,0 +1,37 @@
using System;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components.Deployment
{
/// <inheritdoc />
abstract class DmbProviderBase : IDmbProvider
{
/// <inheritdoc />
public string DmbName => String.Concat(
CompileJob.DmeName,
ByondVersion.Engine.Value switch
{
EngineType.Byond => ".dmb",
EngineType.OpenDream => ".json",
_ => throw new InvalidOperationException($"Invalid EngineType: {ByondVersion.Engine.Value}"),
});
/// <inheritdoc />
public abstract string Directory { get; }
/// <inheritdoc />
public abstract Models.CompileJob CompileJob { get; }
/// <inheritdoc />
public abstract ByondVersion ByondVersion { get; }
/// <inheritdoc />
public abstract void Dispose();
/// <inheritdoc />
public abstract void KeepAlive();
}
}
@@ -30,11 +30,6 @@ namespace Tgstation.Server.Host.Components.Deployment
/// <inheritdoc />
sealed class DreamMaker : IDreamMaker
{
/// <summary>
/// Extension for .dmbs.
/// </summary>
public const string DmbExtension = ".dmb";
/// <summary>
/// Extension for .dmes.
/// </summary>
@@ -805,7 +800,6 @@ namespace Tgstation.Server.Host.Components.Deployment
ApiValidationStatus validationStatus;
using (var provider = new TemporaryDmbProvider(
ioManager.ResolvePath(job.DirectoryName.ToString()),
String.Concat(job.DmeName, DmbExtension),
job,
byondLock.Version))
await using (var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken))
@@ -7,41 +7,36 @@ namespace Tgstation.Server.Host.Components.Deployment
/// <summary>
/// Temporary <see cref="IDmbProvider"/>.
/// </summary>
sealed class TemporaryDmbProvider : IDmbProvider
sealed class TemporaryDmbProvider : DmbProviderBase
{
/// <inheritdoc />
public string DmbName { get; }
public override string Directory { get; }
/// <inheritdoc />
public string Directory { get; }
public override Models.CompileJob CompileJob { get; }
/// <inheritdoc />
public Models.CompileJob CompileJob { get; }
/// <inheritdoc />
public ByondVersion ByondVersion { get; }
public override ByondVersion ByondVersion { get; }
/// <summary>
/// Initializes a new instance of the <see cref="TemporaryDmbProvider"/> class.
/// </summary>
/// <param name="directory">The value of <see cref="Directory"/>.</param>
/// <param name="dmb">The value of <see cref="DmbName"/>.</param>
/// <param name="compileJob">The value of <see cref="CompileJob"/>.</param>
/// <param name="byondVersion">The value of <see cref="ByondVersion"/>.</param>
public TemporaryDmbProvider(string directory, string dmb, Models.CompileJob compileJob, ByondVersion byondVersion)
public TemporaryDmbProvider(string directory, Models.CompileJob compileJob, ByondVersion byondVersion)
{
DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb));
Directory = directory ?? throw new ArgumentNullException(nameof(directory));
CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
ByondVersion = byondVersion ?? throw new ArgumentNullException(nameof(byondVersion));
}
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
}
/// <inheritdoc />
public void KeepAlive() => throw new NotSupportedException();
public override void KeepAlive() => throw new NotSupportedException();
}
}
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
@@ -34,21 +36,23 @@ namespace Tgstation.Server.Host.Components.Engine
/// <summary>
/// Initializes a new instance of the <see cref="OpenDreamInstallation"/> class.
/// </summary>
/// <param name="serverExePath">The value of <see cref="ServerExePath"/>.</param>
/// <param name="compilerExePath">The value of <see cref="CompilerExePath"/>.</param>
/// <param name="installationTask">The value of <see cref="InstallationTask"/>.</param>
/// <param name="version">The value of <see cref="Version"/>.</param>
public OpenDreamInstallation(
string serverExePath,
string compilerExePath,
Task installationTask,
ByondVersion version)
{
ServerExePath = serverExePath ?? throw new ArgumentNullException(nameof(serverExePath));
CompilerExePath = compilerExePath ?? throw new ArgumentNullException(nameof(compilerExePath));
InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
ArgumentNullException.ThrowIfNull(version);
if (version.Engine.Value != EngineType.OpenDream)
throw new ArgumentException($"Invalid EngineType: {version.Engine.Value}", nameof(version));
Version = version ?? throw new ArgumentNullException(nameof(version));
throw new NotImplementedException();
}
/// <inheritdoc />
@@ -58,14 +62,33 @@ namespace Tgstation.Server.Host.Components.Engine
ArgumentNullException.ThrowIfNull(parameters);
ArgumentNullException.ThrowIfNull(launchParameters);
throw new NotImplementedException();
var parametersString = String.Join(';', parameters.Select(kvp => $"{kvp.Key}={kvp.Value}"));
if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
{
// TGS and BYOND expect url encoded params, OD takes unencoded
var unencodedAdditionalParams = String.Join(
';',
launchParameters
.AdditionalParameters
.Split('&')
.Select(
singleParam => String.Join(
'=',
singleParam
.Split('=')
.Select(
encodedParam => HttpUtility.UrlDecode(encodedParam)))));
parametersString = $"{parametersString};{unencodedAdditionalParams}";
}
var arguments = $"--cvar net.port={launchParameters.Port.Value} --cvar opendream.topic_port=0 --cvar opendream.world_params=\"{parametersString}\" \"{dmbProvider.DmbName}\"";
return arguments;
}
/// <inheritdoc />
public string FormatCompilerArguments(string dmePath)
{
ArgumentNullException.ThrowIfNull(dmePath);
throw new NotImplementedException();
}
=> $"--suppress-unimplemented --verbose --notices-enabled {dmePath ?? throw new ArgumentNullException(nameof(dmePath))}";
}
}
@@ -23,10 +23,15 @@ namespace Tgstation.Server.Host.Components.Engine
/// </summary>
sealed class OpenDreamInstaller : EngineInstallerBase
{
/// <summary>
/// The name of the subdirectory used to store the server and compiler binaries.
/// </summary>
const string InstallationBinDirectory = "bin";
/// <summary>
/// The name of the subdirectory used for the <see cref="RepositoryEngineInstallationData"/>'s copy.
/// </summary>
private const string InstallationRepositorySubDirectory = "SourceRepo";
const string InstallationSourceSubDirectory = "TgsSourceSubdir";
/// <inheritdoc />
protected override EngineType TargetEngineType => EngineType.OpenDream;
@@ -82,7 +87,21 @@ namespace Tgstation.Server.Host.Components.Engine
public override IEngineInstallation CreateInstallation(ByondVersion version, Task installationTask)
{
CheckVersionValidity(version);
return new OpenDreamInstallation(installationTask, version);
var binPathForVersion = IOManager.ConcatPath(version.ToString(), InstallationBinDirectory);
var exeExtension = platformIdentifier.IsWindows
? ".exe"
: String.Empty;
return new OpenDreamInstallation(
IOManager.ConcatPath(
binPathForVersion,
$"OpenDreamServer{exeExtension}"),
IOManager.ConcatPath(
binPathForVersion,
$"DMCompiler{exeExtension}"),
installationTask,
version);
}
/// <inheritdoc />
@@ -135,7 +154,7 @@ namespace Tgstation.Server.Host.Components.Engine
version.SourceCommittish = repo.Head;
return new RepositoryEngineInstallationData(IOManager, repo, InstallationRepositorySubDirectory);
return new RepositoryEngineInstallationData(IOManager, repo, InstallationSourceSubDirectory);
}
catch
{
@@ -149,6 +168,40 @@ namespace Tgstation.Server.Host.Components.Engine
{
CheckVersionValidity(version);
ArgumentNullException.ThrowIfNull(installPath);
var sourcePath = IOManager.ConcatPath(installPath, InstallationSourceSubDirectory);
if (!await IOManager.DirectoryExists(sourcePath, cancellationToken))
{
// a zip install that didn't come from us?
// we want to use the bin dir, so put everything where we expect
Logger.LogDebug("Correcting extraction location...");
var dirsTask = IOManager.GetDirectories(installPath, cancellationToken);
var filesTask = IOManager.GetFiles(installPath, cancellationToken);
var dirCreateTask = IOManager.CreateDirectory(sourcePath, cancellationToken);
await Task.WhenAll(dirsTask, filesTask, dirCreateTask);
var dirsMoveTasks = dirsTask
.Result
.Select(
dirPath => IOManager.MoveDirectory(
dirPath,
IOManager.ConcatPath(
sourcePath,
IOManager.GetFileName(sourcePath)),
cancellationToken));
var filesMoveTask = filesTask
.Result
.Select(
filePath => IOManager.MoveFile(
filePath,
IOManager.ConcatPath(
sourcePath,
IOManager.GetFileName(sourcePath)),
cancellationToken));
await Task.WhenAll(dirsMoveTasks.Concat(filesMoveTask));
}
var dotnetPaths = DotnetHelper.GetPotentialDotnetPaths(platformIdentifier.IsWindows)
.ToList();
@@ -165,11 +218,9 @@ namespace Tgstation.Server.Host.Components.Engine
var dotnetPath = dotnetPaths[selectedPathIndex];
var repositoryPath = IOManager.ConcatPath(installPath, InstallationRepositorySubDirectory);
await using (var buildProcess = processExecutor.LaunchProcess(
dotnetPath,
repositoryPath,
sourcePath,
"build -c Release",
null,
true,
@@ -180,18 +231,17 @@ namespace Tgstation.Server.Host.Components.Engine
throw new JobException("OpenDream build failed!");
}
const string BinDirectory = "bin";
await IOManager.MoveDirectory(
IOManager.ConcatPath(
repositoryPath,
BinDirectory,
sourcePath,
InstallationBinDirectory,
"Content.Server"),
IOManager.ConcatPath(
installPath,
BinDirectory),
InstallationBinDirectory),
cancellationToken);
await IOManager.DeleteDirectory(repositoryPath, cancellationToken);
await IOManager.DeleteDirectory(sourcePath, cancellationToken);
}
/// <inheritdoc />
@@ -8,7 +8,7 @@
/// <summary>
/// DreamDaemon notifying us of its current port and requesting a change if necessary.
/// </summary>
PortUpdate,
DeprecatedPortUpdate,
/// <summary>
/// DreamDaemon notifying it is starting.
@@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge
public BridgeCommandType? CommandType { get; set; }
/// <summary>
/// The current port for <see cref="BridgeCommandType.PortUpdate"/> requests.
/// The current port for <see cref="BridgeCommandType.DeprecatedPortUpdate"/> requests.
/// </summary>
public ushort? CurrentPort { get; set; }
@@ -45,5 +45,10 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge
/// The <see cref="ChunkData"/> for <see cref="BridgeCommandType.Chunk"/> requests.
/// </summary>
public ChunkData Chunk { get; set; }
/// <summary>
/// The port that should be used to send world topics, if not the default.
/// </summary>
public ushort? TopicPort { get; set; }
}
}
@@ -2,9 +2,9 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Deployment;
using Tgstation.Server.Host.Components.Interop.Topic;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.System;
namespace Tgstation.Server.Host.Components.Session
@@ -37,7 +37,12 @@ namespace Tgstation.Server.Host.Components.Session
/// <summary>
/// Gets the <see cref="CompileJob"/> associated with the <see cref="ISessionController"/>.
/// </summary>
CompileJob CompileJob { get; }
Models.CompileJob CompileJob { get; }
/// <summary>
/// Gets the <see cref="Api.Models.Internal.ByondVersion"/> associated with the <see cref="ISessionController"/>.
/// </summary>
ByondVersion ByondVersion { get; }
/// <summary>
/// Gets the <see cref="Session.ReattachInformation"/> associated with the <see cref="ISessionController"/>.
@@ -98,14 +103,6 @@ namespace Tgstation.Server.Host.Components.Session
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="TopicResponse"/> of /world/Topic().</returns>
ValueTask<TopicResponse> SendCommand(TopicParameters parameters, CancellationToken cancellationToken);
/// <summary>
/// Causes the world to start listening on a <paramref name="newPort"/>.
/// </summary>
/// <param name="newPort">The port to change to.</param>
/// <param name="cancellatonToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in <see langword="true"/> if the operation succeeded, <see langword="false"/> otherwise.</returns>
Task<bool> SetPort(ushort newPort, CancellationToken cancellatonToken);
/// <summary>
/// Attempts to change the current <see cref="RebootState"/> to <paramref name="newRebootState"/>.
/// </summary>
@@ -14,6 +14,7 @@ using Newtonsoft.Json;
using Serilog.Context;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment;
@@ -51,6 +52,9 @@ namespace Tgstation.Server.Host.Components.Session
/// <inheritdoc />
public Models.CompileJob CompileJob => ReattachInformation.Dmb.CompileJob;
/// <inheritdoc />
public ByondVersion ByondVersion => ReattachInformation.Dmb.ByondVersion;
/// <inheritdoc />
public RebootState RebootState => ReattachInformation.RebootState;
@@ -167,11 +171,6 @@ namespace Tgstation.Server.Host.Components.Session
/// </summary>
readonly object synchronizationLock;
/// <summary>
/// The <see cref="TaskCompletionSource{TResult}"/> <see cref="SetPort(ushort, CancellationToken)"/> waits on when DreamDaemon currently has it's ports closed.
/// </summary>
TaskCompletionSource<bool> portAssignmentTcs;
/// <summary>
/// The <see cref="TaskCompletionSource"/> that completes when DD sends a valid startup bridge request.
/// </summary>
@@ -197,11 +196,6 @@ namespace Tgstation.Server.Host.Components.Session
/// </summary>
volatile uint rebootBridgeRequestsProcessing;
/// <summary>
/// The port to assign DreamDaemon when it queries for it.
/// </summary>
ushort? nextPort;
/// <summary>
/// The <see cref="ApiValidationStatus"/> for the <see cref="SessionController"/>.
/// </summary>
@@ -484,40 +478,6 @@ namespace Tgstation.Server.Host.Components.Session
return fullResponse;
}
/// <inheritdoc />
public Task<bool> SetPort(ushort port, CancellationToken cancellationToken)
{
CheckDisposed();
if (port == 0)
throw new ArgumentOutOfRangeException(nameof(port), port, "port must not be zero!");
async Task<bool> ImmediateTopicPortChange()
{
var commandResult = await SendCommand(
new TopicParameters(port),
cancellationToken);
if (commandResult?.ErrorMessage != null)
return false;
ReattachInformation.Port = port;
return true;
}
lock (synchronizationLock)
if (portClosedForReboot)
{
if (portAssignmentTcs != null)
throw new InvalidOperationException("A port change operation is already in progress!");
nextPort = port;
portAssignmentTcs = new TaskCompletionSource<bool>();
return portAssignmentTcs.Task;
}
else
return ImmediateTopicPortChange();
}
/// <inheritdoc />
public async ValueTask<bool> SetRebootState(RebootState newRebootState, CancellationToken cancellationToken)
{
@@ -702,37 +662,8 @@ namespace Tgstation.Server.Host.Components.Session
TerminationWasRequested = true;
process.Terminate();
break;
case BridgeCommandType.PortUpdate:
lock (synchronizationLock)
{
if (!parameters.CurrentPort.HasValue)
{
/////UHHHH
Logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
return BridgeError("Missing stringified port as data parameter!");
}
var currentPort = parameters.CurrentPort.Value;
if (!nextPort.HasValue)
ReattachInformation.Port = parameters.CurrentPort.Value; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
else
{
// nextPort is ready, tell DD to switch to that
// if it fails it'll kill itself
response.NewPort = nextPort.Value;
ReattachInformation.Port = nextPort.Value;
nextPort = null;
// we'll also get here from SetPort so complete that task
var tmpTcs = portAssignmentTcs;
portAssignmentTcs = null;
tmpTcs.SetResult(true);
}
portClosedForReboot = false;
}
break;
case BridgeCommandType.DeprecatedPortUpdate:
return BridgeError("Port switching is no longer supported!");
case BridgeCommandType.Startup:
apiValidationStatus = ApiValidationStatus.BadValidationRequest;
if (parameters.Version == null)
@@ -774,6 +705,13 @@ namespace Tgstation.Server.Host.Components.Session
ReattachInformation.RuntimeInformation.ServerPort,
ReattachInformation.RuntimeInformation.ApiValidateOnly);
if (parameters.TopicPort.HasValue)
{
var newTopicPort = parameters.TopicPort.Value;
Logger.LogInformation("Server is requesting use of port {topicPort} for topic communications", newTopicPort);
ReattachInformation.Port = newTopicPort;
}
// Load custom commands
chatTrackingContext.CustomCommands = parameters.CustomCommands;
Interlocked.Exchange(ref startupTcs, new TaskCompletionSource()).SetResult();
@@ -816,7 +754,7 @@ namespace Tgstation.Server.Host.Components.Session
/// <returns>A new errored <see cref="BridgeResponse"/>.</returns>
BridgeResponse BridgeError(string message)
{
Logger.LogWarning("Bridge request chunking error: {message}", message);
Logger.LogWarning("Bridge request error: {message}", message);
return new BridgeResponse
{
ErrorMessage = message,
@@ -255,6 +255,11 @@ namespace Tgstation.Server.Host.Components.Watchdog
await CheckLaunchResult(Server, "Server", cancellationToken);
Server.EnableCustomChatCommands();
// persist again, because the DMAPI can say we need a different topic port (Original OD behavior)
// kinda hacky imo, but at least we can safely forget about this
if (!reattachInProgress)
await SessionPersistor.Save(Server.ReattachInformation, cancellationToken);
}
catch (Exception ex)
{
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Deployment;
@@ -245,6 +246,16 @@ namespace Tgstation.Server.Host.Components.Watchdog
canSeamlesslySwap = false;
}
if (Server.ByondVersion.Engine.Value != EngineType.Byond
|| compileJobProvider.ByondVersion.Engine.Value != EngineType.Byond)
{
Logger.LogDebug(
"Not swapping to new compile job {newCompileJobId} as it or the current compile job ({oldCompileJobId}) is not using the BYOND engine. Queueing graceful restart instead...",
compileJobProvider.CompileJob.Id,
Server.CompileJob.Id);
canSeamlesslySwap = false;
}
if (!canSeamlesslySwap)
{
compileJobProvider.Dispose();