diff --git a/Torch.API/ITorchBase.cs b/Torch.API/ITorchBase.cs
index edf21bb..a89534c 100644
--- a/Torch.API/ITorchBase.cs
+++ b/Torch.API/ITorchBase.cs
@@ -80,12 +80,12 @@ namespace Torch.API
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
///
- /// Start the Torch instance.
+ /// Signals the torch instance to start, then blocks until it's started.
///
void Start();
///
- /// Stop the Torch instance.
+ /// Signals the torch instance to stop, then blocks until it's stopped.
///
void Stop();
diff --git a/Torch.Server/Initializer.cs b/Torch.Server/Initializer.cs
index b025727..020171f 100644
--- a/Torch.Server/Initializer.cs
+++ b/Torch.Server/Initializer.cs
@@ -90,7 +90,7 @@ quit";
{
var ui = new TorchUI(_server);
if (_config.Autostart)
- new Thread(_server.Start).Start();
+ _server.Start();
ui.ShowDialog();
}
else
diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs
index d97d6fe..e40693e 100644
--- a/Torch.Server/TorchServer.cs
+++ b/Torch.Server/TorchServer.cs
@@ -146,6 +146,7 @@ namespace Torch.Server
}
}
+ ///
public override void Start()
{
if (State != ServerState.Stopped)
@@ -176,17 +177,19 @@ namespace Torch.Server
}
///
- /// Restart the program. DOES NOT SAVE!
+ /// Restart the program.
///
public override void Restart()
{
- var exe = Assembly.GetExecutingAssembly().Location;
- ((TorchConfig) Config).WaitForPID = Process.GetCurrentProcess().Id.ToString();
- Config.Autostart = true;
- Process.Start(exe, Config.ToString());
Save(0).Wait();
Stop();
LogManager.Flush();
+
+ var exe = Assembly.GetExecutingAssembly().Location;
+ ((TorchConfig)Config).WaitForPID = Process.GetCurrentProcess().Id.ToString();
+ Config.Autostart = true;
+ Process.Start(exe, Config.ToString());
+
Process.GetCurrentProcess().Kill();
}
diff --git a/Torch.Server/Views/TorchUI.xaml.cs b/Torch.Server/Views/TorchUI.xaml.cs
index 92b1083..1bee4c3 100644
--- a/Torch.Server/Views/TorchUI.xaml.cs
+++ b/Torch.Server/Views/TorchUI.xaml.cs
@@ -66,7 +66,7 @@ namespace Torch.Server
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
_server.GetManager().SaveConfig();
- new Thread(_server.Start).Start();
+ _server.Start();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
diff --git a/Torch/Torch.csproj b/Torch/Torch.csproj
index a77ba4a..d999355 100644
--- a/Torch/Torch.csproj
+++ b/Torch/Torch.csproj
@@ -255,6 +255,7 @@
CollectionEditor.xaml
+
diff --git a/Torch/TorchBase.cs b/Torch/TorchBase.cs
index a187169..9160108 100644
--- a/Torch/TorchBase.cs
+++ b/Torch/TorchBase.cs
@@ -343,7 +343,9 @@ namespace Torch
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
- InitVRageInstance();
+ _game = new VRageGame(this, TweakGameSettings, SteamAppName, SteamAppId, Config.InstancePath, RunArgs);
+ if (!_game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromMinutes(5)))
+ Log.Warn("Failed to wait for game to be initialized");
Managers.GetManager().LoadPlugins();
Managers.Attach();
_init = true;
@@ -357,95 +359,15 @@ namespace Torch
public virtual void Dispose()
{
Managers.Detach();
- DisposeVRageInstance();
+ _game.SignalDestroy();
+ if (!_game.WaitFor(VRageGame.GameState.Destroyed, TimeSpan.FromSeconds(15)))
+ Log.Warn("Failed to wait for the game to be destroyed");
+ _game = null;
}
#endregion
- #region VRage Instance Init/Destroy
-
-#pragma warning disable 649
- [ReflectedGetter(Name = "m_plugins", Type = typeof(MyPlugins))]
- private static readonly Func> _getVRagePluginList;
-#pragma warning restore 649
-
- protected SpaceEngineersGame GameInstance { get; private set; }
-
- ///
- /// Sets up the VRage instance.
- /// Any flags (ie ) must be set before this is called.
- ///
- protected virtual void InitVRageInstance()
- {
- bool dedicated = Sandbox.Engine.Platform.Game.IsDedicated;
- Environment.SetEnvironmentVariable("SteamAppId", SteamAppId.ToString());
- MyServiceManager.Instance.AddService(new MySteamService(dedicated, SteamAppId));
- if (dedicated && !MyGameService.HasGameServer)
- {
- Log.Warn("Steam service is not running! Please reinstall dedicated server.");
- return;
- }
-
- SpaceEngineersGame.SetupBasicGameInfo();
- SpaceEngineersGame.SetupPerGameSettings();
- MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
- MySessionComponentExtDebug.ForceDisable = true;
- MyPerGameSettings.SendLogToKeen = false;
- // SpaceEngineersGame.SetupAnalytics();
-
- MyFileSystem.ExePath = Path.GetDirectoryName(typeof(SpaceEngineersGame).Assembly.Location);
-
- TweakGameSettings();
-
- MyInitializer.InvokeBeforeRun(SteamAppId, SteamAppName, Config.InstancePath);
- // MyInitializer.InitCheckSum();
-
-
- // Hook into the VRage plugin system for updates.
- _getVRagePluginList().Add(this);
-
- if (!MySandboxGame.IsReloading)
- MyFileSystem.InitUserSpecific(dedicated ? null : MyGameService.UserId.ToString());
- MySandboxGame.IsReloading = dedicated;
-
- // render init
- {
- IMyRender renderer = null;
- if (dedicated)
- {
- renderer = new MyNullRender();
- }
- else
- {
- MyPerformanceSettings preset = MyGuiScreenOptionsGraphics.GetPreset(MyRenderQualityEnum.NORMAL);
- MyRenderProxy.Settings.User = MyVideoSettingsManager.GetGraphicsSettingsFromConfig(ref preset)
- .PerformanceSettings.RenderSettings;
- MyStringId graphicsRenderer = MySandboxGame.Config.GraphicsRenderer;
- if (graphicsRenderer == MySandboxGame.DirectX11RendererKey)
- {
- renderer = new MyDX11Render(new MyRenderSettings?(MyRenderProxy.Settings));
- if (!renderer.IsSupported)
- {
- MySandboxGame.Log.WriteLine(
- "DirectX 11 renderer not supported. No renderer to revert back to.");
- renderer = null;
- }
- }
- if (renderer == null)
- {
- throw new MyRenderException(
- "The current version of the game requires a Dx11 card. \\n For more information please see : http://blog.marekrosa.org/2016/02/space-engineers-news-full-source-code_26.html",
- MyRenderExceptionEnum.GpuNotSupported);
- }
- MySandboxGame.Config.GraphicsRenderer = graphicsRenderer;
- }
- MyRenderProxy.Initialize(renderer);
- MyRenderProxy.GetRenderProfiler().SetAutocommit(false);
- MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint");
- }
-
- GameInstance = new SpaceEngineersGame(RunArgs);
- }
+ private VRageGame _game;
///
/// Called after the basic game information is filled, but before the game is created.
@@ -453,23 +375,7 @@ namespace Torch
protected virtual void TweakGameSettings()
{
}
-
- ///
- /// Tears down the VRage instance
- ///
- protected virtual void DisposeVRageInstance()
- {
- GameInstance.Dispose();
- GameInstance = null;
-
- MyGameService.ShutDown();
-
- _getVRagePluginList().Remove(this);
-
- MyInitializer.InvokeAfterRun();
- }
-
- #endregion
+
///
public virtual Task Save(long callerId)
@@ -480,21 +386,17 @@ namespace Torch
///
public virtual void Start()
{
- if (MySandboxGame.FatalErrorDuringInit)
- {
- Log.Warn($"Failed to start sandbox game: fatal error during init");
- return;
- }
- GameInstance.Run();
+ _game.SignalStart();
+ if (!_game.WaitFor(VRageGame.GameState.Running, TimeSpan.FromSeconds(15)))
+ Log.Warn("Failed to wait for the game to be started");
}
///
public virtual void Stop()
{
- if (IsOnGameThread())
- MySandboxGame.Static.Exit();
- else
- InvokeBlocking(MySandboxGame.Static.Exit);
+ _game.SignalStop();
+ if (!_game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromSeconds(15)))
+ Log.Warn("Failed to wait for the game to be stopped");
}
///
diff --git a/Torch/VRageGame.cs b/Torch/VRageGame.cs
new file mode 100644
index 0000000..e43b8ee
--- /dev/null
+++ b/Torch/VRageGame.cs
@@ -0,0 +1,268 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Havok;
+using NLog;
+using NLog.Fluent;
+using Sandbox;
+using Sandbox.Engine.Networking;
+using Sandbox.Engine.Platform.VideoMode;
+using Sandbox.Game;
+using SpaceEngineers.Game;
+using SpaceEngineers.Game.GUI;
+using Torch.Utils;
+using VRage;
+using VRage.FileSystem;
+using VRage.Game;
+using VRage.Game.SessionComponents;
+using VRage.GameServices;
+using VRage.Plugins;
+using VRage.Steam;
+using VRage.Utils;
+using VRageRender;
+
+namespace Torch
+{
+ public class VRageGame
+ {
+ private static readonly ILogger _log = LogManager.GetCurrentClassLogger();
+
+#pragma warning disable 649
+ [ReflectedGetter(Name = "m_plugins", Type = typeof(MyPlugins))]
+ private static readonly Func> _getVRagePluginList;
+#pragma warning restore 649
+
+ private readonly TorchBase _torch;
+ private readonly Action _tweakGameSettings;
+ private readonly string _userDataPath;
+ private readonly string _appName;
+ private readonly uint _appSteamId;
+ private readonly string[] _runArgs;
+ private SpaceEngineersGame _game;
+ private readonly Thread _updateThread;
+
+ private bool _startGame = false;
+ private readonly AutoResetEvent _commandChanged = new AutoResetEvent(false);
+ private bool _destroyGame = false;
+
+ private readonly AutoResetEvent _stateChangedEvent = new AutoResetEvent(false);
+ private GameState _state;
+
+ public enum GameState
+ {
+ Creating,
+ Stopped,
+ Running,
+ Destroyed
+ }
+
+ internal VRageGame(TorchBase torch, Action tweakGameSettings, string appName, uint appSteamId,
+ string userDataPath, string[] runArgs)
+ {
+ _torch = torch;
+ _tweakGameSettings = tweakGameSettings;
+ _appName = appName;
+ _appSteamId = appSteamId;
+ _userDataPath = userDataPath;
+ _runArgs = runArgs;
+ _updateThread = new Thread(Run);
+ _updateThread.Start();
+ }
+
+ private void StateChange(GameState s)
+ {
+ if (_state == s)
+ return;
+ _state = s;
+ _stateChangedEvent.Set();
+ }
+
+ private void Run()
+ {
+ StateChange(GameState.Creating);
+ try
+ {
+ Create();
+ _destroyGame = false;
+ while (!_destroyGame)
+ {
+ StateChange(GameState.Stopped);
+ _commandChanged.WaitOne();
+ if (_startGame)
+ {
+ _startGame = false;
+ DoStart();
+ }
+ }
+ }
+ finally
+ {
+ Destroy();
+ StateChange(GameState.Destroyed);
+ }
+ }
+
+ private void Create()
+ {
+ bool dedicated = Sandbox.Engine.Platform.Game.IsDedicated;
+ Environment.SetEnvironmentVariable("SteamAppId", _appSteamId.ToString());
+ MyServiceManager.Instance.AddService(new MySteamService(dedicated, _appSteamId));
+ if (dedicated && !MyGameService.HasGameServer)
+ {
+ _log.Warn("Steam service is not running! Please reinstall dedicated server.");
+ return;
+ }
+
+ SpaceEngineersGame.SetupBasicGameInfo();
+ SpaceEngineersGame.SetupPerGameSettings();
+ MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
+ MySessionComponentExtDebug.ForceDisable = true;
+ MyPerGameSettings.SendLogToKeen = false;
+ // SpaceEngineersGame.SetupAnalytics();
+
+ MyFileSystem.ExePath = Path.GetDirectoryName(typeof(SpaceEngineersGame).Assembly.Location);
+
+ _tweakGameSettings();
+
+ MyFileSystem.Reset();
+ MyInitializer.InvokeBeforeRun(_appSteamId, _appName, _userDataPath);
+ // MyInitializer.InitCheckSum();
+
+
+ // Hook into the VRage plugin system for updates.
+ _getVRagePluginList().Add(_torch);
+
+ if (!MySandboxGame.IsReloading)
+ MyFileSystem.InitUserSpecific(dedicated ? null : MyGameService.UserId.ToString());
+ MySandboxGame.IsReloading = dedicated;
+
+ // render init
+ {
+ IMyRender renderer = null;
+ if (dedicated)
+ {
+ renderer = new MyNullRender();
+ }
+ else
+ {
+ MyPerformanceSettings preset = MyGuiScreenOptionsGraphics.GetPreset(MyRenderQualityEnum.NORMAL);
+ MyRenderProxy.Settings.User = MyVideoSettingsManager.GetGraphicsSettingsFromConfig(ref preset)
+ .PerformanceSettings.RenderSettings;
+ MyStringId graphicsRenderer = MySandboxGame.Config.GraphicsRenderer;
+ if (graphicsRenderer == MySandboxGame.DirectX11RendererKey)
+ {
+ renderer = new MyDX11Render(new MyRenderSettings?(MyRenderProxy.Settings));
+ if (!renderer.IsSupported)
+ {
+ MySandboxGame.Log.WriteLine(
+ "DirectX 11 renderer not supported. No renderer to revert back to.");
+ renderer = null;
+ }
+ }
+ if (renderer == null)
+ {
+ throw new MyRenderException(
+ "The current version of the game requires a Dx11 card. \\n For more information please see : http://blog.marekrosa.org/2016/02/space-engineers-news-full-source-code_26.html",
+ MyRenderExceptionEnum.GpuNotSupported);
+ }
+ MySandboxGame.Config.GraphicsRenderer = graphicsRenderer;
+ }
+ MyRenderProxy.Initialize(renderer);
+ MyRenderProxy.GetRenderProfiler().SetAutocommit(false);
+ MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint");
+ }
+
+ _game = new SpaceEngineersGame(_runArgs);
+ }
+
+ private void Destroy()
+ {
+ _game.Dispose();
+ _game = null;
+
+ MyGameService.ShutDown();
+
+ _getVRagePluginList().Remove(_torch);
+
+ MyInitializer.InvokeAfterRun();
+ }
+
+ private void DoStart()
+ {
+ if (MySandboxGame.FatalErrorDuringInit)
+ {
+ _log.Warn("Failed to start sandbox game: fatal error during init");
+ return;
+ }
+ try
+ {
+ StateChange(GameState.Running);
+ _game.Run();
+ }
+ finally
+ {
+ StateChange(GameState.Stopped);
+ }
+ }
+
+ private void DoStop()
+ {
+ ParallelTasks.Parallel.Scheduler.WaitForTasksToFinish(TimeSpan.FromSeconds(10.0));
+ MySandboxGame.Static.Exit();
+ }
+
+ ///
+ /// Signals the game to stop itself.
+ ///
+ public void SignalStop()
+ {
+ _startGame = false;
+ _game.Invoke(DoStop, $"{nameof(VRageGame)}::{nameof(SignalStop)}");
+ }
+
+ ///
+ /// Signals the game to start itself
+ ///
+ public void SignalStart()
+ {
+ _startGame = true;
+ _commandChanged.Set();
+ }
+
+ ///
+ /// Signals the game to destroy itself
+ ///
+ public void SignalDestroy()
+ {
+ _destroyGame = true;
+ SignalStop();
+ _commandChanged.Set();
+ }
+
+ ///
+ /// Waits for the game to transition to the given state
+ ///
+ /// State to transition to
+ /// Timeout
+ ///
+ public bool WaitFor(GameState state, TimeSpan? timeout)
+ {
+ // Kinda icky, but we can't block the update and expect the state to change.
+ if (Thread.CurrentThread == _updateThread)
+ return _state == state;
+
+ DateTime? end = timeout.HasValue ? (DateTime?) (DateTime.Now + timeout.Value) : null;
+ while (_state != state && (!end.HasValue || end > DateTime.Now + TimeSpan.FromSeconds(1)))
+ if (end.HasValue)
+ _stateChangedEvent.WaitOne(end.Value - DateTime.Now);
+ else
+ _stateChangedEvent.WaitOne();
+ return _state == state;
+ }
+ }
+}
\ No newline at end of file