diff --git a/src/Tgstation.Server.Host.Console/Properties/launchSettings.json b/src/Tgstation.Server.Host.Console/Properties/launchSettings.json
index e88685dcca..6bfbf6e27c 100644
--- a/src/Tgstation.Server.Host.Console/Properties/launchSettings.json
+++ b/src/Tgstation.Server.Host.Console/Properties/launchSettings.json
@@ -10,6 +10,7 @@
"profiles": {
"Tgstation.Server.Host.Console": {
"commandName": "Project",
+ "commandLineArgs": "--attach-host-debugger",
"workingDirectory": "bin\\Debug\\netcoreapp2.1",
"launchBrowser": true,
"environmentVariables": {
diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs
index 3e3ad9aad0..c6292a3641 100644
--- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs
+++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs
@@ -70,8 +70,11 @@ namespace Tgstation.Server.Host.Watchdog
logger.LogInformation("Detected dotnet executable at {0}", dotnetPath);
var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
-
+
var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog
+#if DEBUG
+ Directory.CreateDirectory(assemblyStoragePath);
+#endif
var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default"));
#if DEBUG
//just copy the shit where it belongs
@@ -121,7 +124,8 @@ namespace Tgstation.Server.Host.Watchdog
'"' + assemblyPath + '"',
'"' + updateDirectory + '"'
};
- if (Debugger.IsAttached)
+
+ if (Environment.GetCommandLineArgs().Any(x => x == "--attach-host-debugger"))
arguments.Add("--attach-debugger");
arguments.AddRange(args);
diff --git a/src/Tgstation.Server.Host/Components/Chat/Chat.cs b/src/Tgstation.Server.Host/Components/Chat/Chat.cs
index e11d311609..191988addc 100644
--- a/src/Tgstation.Server.Host/Components/Chat/Chat.cs
+++ b/src/Tgstation.Server.Host/Components/Chat/Chat.cs
@@ -546,7 +546,7 @@ namespace Tgstation.Server.Host.Components.Chat
{
trackingContexts.Add(context);
lock (mappedChannels)
- task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
+ task = context.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken);
}
await task.ConfigureAwait(false);
return context;
diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs
index 1c3a909b98..8b830e8fa7 100644
--- a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs
+++ b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs
@@ -186,6 +186,8 @@ namespace Tgstation.Server.Host.Components.Compiler
///
public async Task FromCompileJob(CompileJob compileJob, CancellationToken cancellationToken)
{
+ if (compileJob == null)
+ throw new ArgumentNullException(nameof(compileJob));
logger.LogTrace("Loading compile job {0}...", compileJob.Id);
var providerSubmitted = false;
var newProvider = new DmbProvider(compileJob, ioManager, () =>
diff --git a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs
index 8dc412f10a..6c666e2a7f 100644
--- a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs
+++ b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Tgstation.Server.Host.Components.Compiler;
using Tgstation.Server.Host.Components.Watchdog;
using Tgstation.Server.Host.Core;
+using Z.EntityFramework.Plus;
namespace Tgstation.Server.Host.Components
{
@@ -56,6 +57,8 @@ namespace Tgstation.Server.Host.Components
logger.LogDebug("Saving reattach information: {0}...", reattachInformation);
+ var deleteTask = db.WatchdogReattachInformations.Where(x => x.InstanceId == metadata.Id).DeleteAsync(cancellationToken);
+
var instance = new Models.Instance { Id = metadata.Id };
db.Instances.Attach(instance);
@@ -84,6 +87,7 @@ namespace Tgstation.Server.Host.Components
Bravo = ConvertReattachInfo(reattachInformation.Bravo),
AlphaIsActive = reattachInformation.AlphaIsActive,
};
+ await deleteTask.ConfigureAwait(false);
await db.Save(cancellationToken).ConfigureAwait(false);
});
@@ -92,8 +96,18 @@ namespace Tgstation.Server.Host.Components
{
Models.WatchdogReattachInformation result = null;
await databaseContextFactory.UseContext(async (db) =>
- result = await db.Instances.Where(x => x.Id == metadata.Id).Select(x => x.WatchdogReattachInformation).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)
- ).ConfigureAwait(false);
+ {
+ var instance = await db.Instances.Where(x => x.Id == metadata.Id)
+ .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha).ThenInclude(x => x.CompileJob)
+ .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo).ThenInclude(x => x.CompileJob)
+ .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+ result = instance.WatchdogReattachInformation;
+ if (result == default)
+ return;
+ instance.WatchdogReattachInformation = null;
+ db.WatchdogReattachInformations.Remove(result);
+ await db.Save(cancellationToken).ConfigureAwait(false);
+ }).ConfigureAwait(false);
if (result == default)
{
diff --git a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs
new file mode 100644
index 0000000000..c310f18d65
--- /dev/null
+++ b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tgstation.Server.Host.Components.Watchdog
+{
+ ///
+ /// Implements a fake "dead"
+ ///
+ sealed class DeadSessionController : ISessionController
+ {
+ ///
+ public Task LaunchResult { get; }
+
+ ///
+ public bool IsPrimary => false;
+
+ ///
+ public bool TerminationWasRequested => false;
+
+ ///
+ public ApiValidationStatus ApiValidationStatus => throw new NotSupportedException();
+
+ ///
+ public IDmbProvider Dmb { get; }
+
+ ///
+ public ushort? Port => null;
+
+ ///
+ public bool ClosePortOnReboot
+ {
+ get => false;
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public RebootState RebootState => throw new NotSupportedException();
+
+ ///
+ public Task OnReboot { get; }
+
+ ///
+ public Task Lifetime { get; }
+
+ ///
+ /// If the was d
+ ///
+ bool disposed;
+
+ ///
+ /// Construct a
+ ///
+ /// The value of
+ public DeadSessionController(IDmbProvider dmbProvider)
+ {
+ Dmb = dmbProvider ?? throw new ArgumentNullException(nameof(dmbProvider));
+ LaunchResult = Task.FromResult(new LaunchResult
+ {
+ StartupTime = TimeSpan.FromSeconds(0)
+ });
+ Lifetime = Task.FromResult(-1);
+ OnReboot = new TaskCompletionSource