diff --git a/Jenkins/jenkins-grab-se.ps1 b/Jenkins/jenkins-grab-se.ps1
index 5699e3d..f94193c 100644
--- a/Jenkins/jenkins-grab-se.ps1
+++ b/Jenkins/jenkins-grab-se.ps1
@@ -17,6 +17,6 @@ if (!(Test-Path $steamCMDPath)) {
}
cd "$steamData"
-& "$steamCMDPath/steamcmd.exe" "+login anonymous" "+force_install_dir $steamData" "+app_update 298740 -beta publictest -betapassword nt8WuDw9kdvE validate" "+quit"
+& "$steamCMDPath/steamcmd.exe" "+login anonymous" "+force_install_dir $steamData" "+app_update 298740 validate" "+quit"
popd
diff --git a/Torch.Server/Initializer.cs b/Torch.Server/Initializer.cs
index 61e7ee6..94d2072 100644
--- a/Torch.Server/Initializer.cs
+++ b/Torch.Server/Initializer.cs
@@ -254,6 +254,7 @@ quit";
private void HandleException(object sender, UnhandledExceptionEventArgs e)
{
+ _server.FatalException = true;
var ex = (Exception)e.ExceptionObject;
LogException(ex);
if (MyFakes.ENABLE_MINIDUMP_SENDING)
diff --git a/Torch.Server/Program.cs b/Torch.Server/Program.cs
index 3e10b1e..2e36d46 100644
--- a/Torch.Server/Program.cs
+++ b/Torch.Server/Program.cs
@@ -63,7 +63,7 @@ namespace Torch.Server
// Breaks on Windows Server 2019
if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive)
{
- using (var service = new TorchService())
+ using (var service = new TorchService(args))
ServiceBase.Run(service);
return;
}
diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs
index 503a277..389e2f8 100644
--- a/Torch.Server/TorchServer.cs
+++ b/Torch.Server/TorchServer.cs
@@ -49,6 +49,8 @@ namespace Torch.Server
private Timer _watchdog;
private int _players;
private MultiplayerManagerDedicated _multiplayerManagerDedicated;
+
+ internal bool FatalException { get; set; }
///
public TorchServer(TorchConfig config = null)
@@ -232,10 +234,16 @@ namespace Torch.Server
private static void CheckServerResponding(object state)
{
+ var server = (TorchServer)state;
var mre = new ManualResetEvent(false);
- ((TorchServer)state).Invoke(() => mre.Set());
+ server.Invoke(() => mre.Set());
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
{
+ if (server.FatalException)
+ {
+ server._watchdog.Dispose();
+ return;
+ }
#if DEBUG
Log.Error(
$"Server watchdog detected that the server was frozen for at least {((TorchServer) state).Config.TickTimeout} seconds.");
diff --git a/Torch.Server/TorchService.cs b/Torch.Server/TorchService.cs
index e33fda3..1a659b8 100644
--- a/Torch.Server/TorchService.cs
+++ b/Torch.Server/TorchService.cs
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceProcess;
+using System.Threading;
using NLog;
using Torch.API;
@@ -12,12 +14,14 @@ namespace Torch.Server
{
class TorchService : ServiceBase
{
+ private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public const string Name = "Torch (SEDS)";
- private TorchServer _server;
private Initializer _initializer;
+ private string[] _args;
- public TorchService()
+ public TorchService(string[] args)
{
+ _args = args;
var workingDir = new FileInfo(typeof(TorchService).Assembly.Location).Directory.ToString();
Directory.SetCurrentDirectory(workingDir);
_initializer = new Initializer(workingDir);
@@ -29,19 +33,21 @@ namespace Torch.Server
}
///
- protected override void OnStart(string[] args)
+ protected override void OnStart(string[] _)
{
- base.OnStart(args);
+ base.OnStart(_args);
- _initializer.Initialize(args);
+ _initializer.Initialize(_args);
_initializer.Run();
}
///
protected override void OnStop()
{
- _server.Stop();
- base.OnStop();
+ var mre = new ManualResetEvent(false);
+ Task.Run(() => _initializer.Server.Stop());
+ if (!mre.WaitOne(TimeSpan.FromMinutes(1)))
+ Process.GetCurrentProcess().Kill();
}
}
}
diff --git a/Torch/Commands/TorchCommands.cs b/Torch/Commands/TorchCommands.cs
index 10ed52e..52733be 100644
--- a/Torch/Commands/TorchCommands.cs
+++ b/Torch/Commands/TorchCommands.cs
@@ -29,6 +29,8 @@ namespace Torch.Commands
{
private static bool _restartPending = false;
private static bool _cancelRestart = false;
+ private bool _stopPending = false;
+ private bool _cancelStop = false;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
[Command("whatsmyip")]
@@ -154,9 +156,25 @@ namespace Torch.Commands
}
[Command("stop", "Stops the server.")]
- public void Stop(bool save = true)
+ public void Stop(bool save = true, int countdownSeconds = 0)
{
- Context.Respond("Stopping server.");
+ if (_stopPending)
+ {
+ Context.Respond("A stop is already pending.");
+ return;
+ }
+
+ _stopPending = true;
+ Task.Run(() =>
+ {
+ var countdown = StopCountdown(countdownSeconds, save).GetEnumerator();
+ while (countdown.MoveNext())
+ {
+ Thread.Sleep(1000);
+ }
+ });
+
+ /*Context.Respond("Stopping server.");
if (save)
DoSave()?.ContinueWith((a, mod) =>
{
@@ -165,7 +183,7 @@ namespace Torch.Commands
torch.Stop();
}, this, TaskContinuationOptions.RunContinuationsAsynchronously);
else
- Context.Torch.Stop();
+ Context.Torch.Stop();*/
}
[Command("restart", "Restarts the server.")]
@@ -204,6 +222,69 @@ namespace Torch.Commands
else
Context.Respond("A restart is not pending.");
}
+
+ [Command("stop cancel", "Cancel a pending stop.")]
+ public void CancelStop()
+ {
+ if (_restartPending)
+ _cancelStop = true;
+ else
+ Context.Respond("Server Stop is not pending.");
+ }
+
+ private IEnumerable StopCountdown(int countdown, bool save)
+ {
+ for (var i = countdown; i >= 0; i--)
+ {
+ if (_cancelStop)
+ {
+ Context.Torch.CurrentSession.Managers.GetManager()
+ .SendMessageAsSelf($"Stop cancelled.");
+
+ _stopPending = false;
+ _cancelStop = false;
+ yield break;
+ }
+
+ if (i >= 60 && i % 60 == 0)
+ {
+ Context.Torch.CurrentSession.Managers.GetManager()
+ .SendMessageAsSelf($"Stopping server in {i / 60} minute{Pluralize(i / 60)}.");
+ yield return null;
+ }
+ else if (i > 0)
+ {
+ if (i < 11)
+ Context.Torch.CurrentSession.Managers.GetManager()
+ .SendMessageAsSelf($"Stopping server in {i} second{Pluralize(i)}.");
+ yield return null;
+ }
+ else
+ {
+ if (save)
+ {
+ Log.Info("Saving game before stop.");
+ Context.Torch.CurrentSession.Managers.GetManager()
+ .SendMessageAsSelf($"Saving game before stop.");
+ DoSave()?.ContinueWith((a, mod) =>
+ {
+ ITorchBase torch = (mod as CommandModule)?.Context?.Torch;
+ Debug.Assert(torch != null);
+ torch.Stop();
+ }, this, TaskContinuationOptions.RunContinuationsAsynchronously);
+
+ }
+ else
+ {
+ Log.Info("Stopping server.");
+ Context.Torch.Invoke(() => Context.Torch.Stop());
+ }
+
+
+ yield break;
+ }
+ }
+ }
private IEnumerable RestartCountdown(int countdown, bool save)
{
diff --git a/Torch/VRageGame.cs b/Torch/VRageGame.cs
index 0f0530f..4f95c4d 100644
--- a/Torch/VRageGame.cs
+++ b/Torch/VRageGame.cs
@@ -199,7 +199,8 @@ namespace Torch
}
MyRenderProxy.Initialize(renderer);
MyRenderProfiler.SetAutocommit(false);
- MyRenderProfiler.InitMemoryHack("MainEntryPoint");
+ //This broke services?
+ //MyRenderProfiler.InitMemoryHack("MainEntryPoint");
}
// Loads object builder serializers. Intuitive, right?