Merge branch 'master' into Patron

This commit is contained in:
John Gross
2019-09-19 11:34:09 -07:00
7 changed files with 111 additions and 14 deletions

View File

@@ -17,6 +17,6 @@ if (!(Test-Path $steamCMDPath)) {
} }
cd "$steamData" 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 popd

View File

@@ -254,6 +254,7 @@ quit";
private void HandleException(object sender, UnhandledExceptionEventArgs e) private void HandleException(object sender, UnhandledExceptionEventArgs e)
{ {
_server.FatalException = true;
var ex = (Exception)e.ExceptionObject; var ex = (Exception)e.ExceptionObject;
LogException(ex); LogException(ex);
if (MyFakes.ENABLE_MINIDUMP_SENDING) if (MyFakes.ENABLE_MINIDUMP_SENDING)

View File

@@ -63,7 +63,7 @@ namespace Torch.Server
// Breaks on Windows Server 2019 // Breaks on Windows Server 2019
if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive) if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive)
{ {
using (var service = new TorchService()) using (var service = new TorchService(args))
ServiceBase.Run(service); ServiceBase.Run(service);
return; return;
} }

View File

@@ -49,6 +49,8 @@ namespace Torch.Server
private Timer _watchdog; private Timer _watchdog;
private int _players; private int _players;
private MultiplayerManagerDedicated _multiplayerManagerDedicated; private MultiplayerManagerDedicated _multiplayerManagerDedicated;
internal bool FatalException { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public TorchServer(TorchConfig config = null) public TorchServer(TorchConfig config = null)
@@ -232,10 +234,16 @@ namespace Torch.Server
private static void CheckServerResponding(object state) private static void CheckServerResponding(object state)
{ {
var server = (TorchServer)state;
var mre = new ManualResetEvent(false); var mre = new ManualResetEvent(false);
((TorchServer)state).Invoke(() => mre.Set()); server.Invoke(() => mre.Set());
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout))) if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
{ {
if (server.FatalException)
{
server._watchdog.Dispose();
return;
}
#if DEBUG #if DEBUG
Log.Error( Log.Error(
$"Server watchdog detected that the server was frozen for at least {((TorchServer) state).Config.TickTimeout} seconds."); $"Server watchdog detected that the server was frozen for at least {((TorchServer) state).Config.TickTimeout} seconds.");

View File

@@ -1,10 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.ServiceProcess; using System.ServiceProcess;
using System.Threading;
using NLog; using NLog;
using Torch.API; using Torch.API;
@@ -12,12 +14,14 @@ namespace Torch.Server
{ {
class TorchService : ServiceBase class TorchService : ServiceBase
{ {
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public const string Name = "Torch (SEDS)"; public const string Name = "Torch (SEDS)";
private TorchServer _server;
private Initializer _initializer; private Initializer _initializer;
private string[] _args;
public TorchService() public TorchService(string[] args)
{ {
_args = args;
var workingDir = new FileInfo(typeof(TorchService).Assembly.Location).Directory.ToString(); var workingDir = new FileInfo(typeof(TorchService).Assembly.Location).Directory.ToString();
Directory.SetCurrentDirectory(workingDir); Directory.SetCurrentDirectory(workingDir);
_initializer = new Initializer(workingDir); _initializer = new Initializer(workingDir);
@@ -29,19 +33,21 @@ namespace Torch.Server
} }
/// <inheritdoc /> /// <inheritdoc />
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(); _initializer.Run();
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void OnStop() protected override void OnStop()
{ {
_server.Stop(); var mre = new ManualResetEvent(false);
base.OnStop(); Task.Run(() => _initializer.Server.Stop());
if (!mre.WaitOne(TimeSpan.FromMinutes(1)))
Process.GetCurrentProcess().Kill();
} }
} }
} }

View File

@@ -29,6 +29,8 @@ namespace Torch.Commands
{ {
private static bool _restartPending = false; private static bool _restartPending = false;
private static bool _cancelRestart = false; private static bool _cancelRestart = false;
private bool _stopPending = false;
private bool _cancelStop = false;
private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static readonly Logger Log = LogManager.GetCurrentClassLogger();
[Command("whatsmyip")] [Command("whatsmyip")]
@@ -154,9 +156,25 @@ namespace Torch.Commands
} }
[Command("stop", "Stops the server.")] [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) if (save)
DoSave()?.ContinueWith((a, mod) => DoSave()?.ContinueWith((a, mod) =>
{ {
@@ -165,7 +183,7 @@ namespace Torch.Commands
torch.Stop(); torch.Stop();
}, this, TaskContinuationOptions.RunContinuationsAsynchronously); }, this, TaskContinuationOptions.RunContinuationsAsynchronously);
else else
Context.Torch.Stop(); Context.Torch.Stop();*/
} }
[Command("restart", "Restarts the server.")] [Command("restart", "Restarts the server.")]
@@ -204,6 +222,69 @@ namespace Torch.Commands
else else
Context.Respond("A restart is not pending."); 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<IChatManagerClient>()
.SendMessageAsSelf($"Stop cancelled.");
_stopPending = false;
_cancelStop = false;
yield break;
}
if (i >= 60 && i % 60 == 0)
{
Context.Torch.CurrentSession.Managers.GetManager<IChatManagerClient>()
.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<IChatManagerClient>()
.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<IChatManagerClient>()
.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) private IEnumerable RestartCountdown(int countdown, bool save)
{ {

View File

@@ -199,7 +199,8 @@ namespace Torch
} }
MyRenderProxy.Initialize(renderer); MyRenderProxy.Initialize(renderer);
MyRenderProfiler.SetAutocommit(false); MyRenderProfiler.SetAutocommit(false);
MyRenderProfiler.InitMemoryHack("MainEntryPoint"); //This broke services?
//MyRenderProfiler.InitMemoryHack("MainEntryPoint");
} }
// Loads object builder serializers. Intuitive, right? // Loads object builder serializers. Intuitive, right?