diff --git a/Torch.API/ITorchBase.cs b/Torch.API/ITorchBase.cs
index 3141edd..1b553fe 100644
--- a/Torch.API/ITorchBase.cs
+++ b/Torch.API/ITorchBase.cs
@@ -74,15 +74,23 @@ namespace Torch.API
///
/// Invoke an action on the game thread and block until it has completed.
- /// If this is called on the game thread the action will execute immediately.
///
- void InvokeBlocking(Action action, [CallerMemberName] string caller = "");
+ /// Action to execute
+ /// Caller of the invoke function
+ /// Timeout before is thrown, or -1 to never timeout
+ /// If the action times out
+ void InvokeBlocking(Action action, int timeoutMs = -1, [CallerMemberName] string caller = "");
///
/// Invoke an action on the game thread asynchronously.
///
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
+ ///
+ /// Invoke a function on the game thread asynchronously.
+ ///
+ Task InvokeAsync(Func func, [CallerMemberName] string caller = "");
+
///
/// Signals the torch instance to start, then blocks until it's started.
///
diff --git a/Torch/Torch.csproj b/Torch/Torch.csproj
index 78c290b..870865d 100644
--- a/Torch/Torch.csproj
+++ b/Torch/Torch.csproj
@@ -198,7 +198,6 @@
-
diff --git a/Torch/TorchBase.cs b/Torch/TorchBase.cs
index 4a0c7ea..e91a52d 100644
--- a/Torch/TorchBase.cs
+++ b/Torch/TorchBase.cs
@@ -226,56 +226,90 @@ namespace Torch
MySandboxGame.Static.Invoke(action, caller);
}
- ///
- /// Invokes an action on the game thread asynchronously.
- ///
- ///
+
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void InvokeBlocking(Action action, int timeoutMs = -1, [CallerMemberName] string caller = "")
+ {
+ // ReSharper disable once ExplicitCallerInfoArgument
+ if (!InvokeAsync(action, caller).Wait(timeoutMs))
+ throw new TimeoutException("The game action timed out");
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public Task InvokeAsync(Func action, [CallerMemberName] string caller = "")
+ {
+ if (Thread.CurrentThread == MySandboxGame.Static.UpdateThread)
+ {
+ Debug.Assert(false, $"{nameof(InvokeAsync)} should not be called on the game thread.");
+ // ReSharper disable once HeuristicUnreachableCode
+ try
+ {
+ return Task.FromResult(action.Invoke());
+ }
+ catch (Exception e)
+ {
+ return Task.FromException(e);
+ }
+ }
+ var ctx = new TaskCompletionSource();
+ MySandboxGame.Static.Invoke(() =>
+ {
+ try
+ {
+ ctx.SetResult(action.Invoke());
+ }
+ catch (Exception e)
+ {
+ ctx.SetException(e);
+ }
+ finally
+ {
+ Debug.Assert(ctx.Task.IsCompleted);
+ }
+ }, caller);
+ return ctx.Task;
+
+ }
+
+
+ ///
[MethodImpl(MethodImplOptions.NoInlining)]
public Task InvokeAsync(Action action, [CallerMemberName] string caller = "")
{
if (Thread.CurrentThread == MySandboxGame.Static.UpdateThread)
{
Debug.Assert(false, $"{nameof(InvokeAsync)} should not be called on the game thread.");
- action?.Invoke();
- return Task.CompletedTask;
+ // ReSharper disable once HeuristicUnreachableCode
+ try
+ {
+ action.Invoke();
+ return Task.CompletedTask;
+ }
+ catch (Exception e)
+ {
+ return Task.FromException(e);
+ }
}
-
- return Task.Run(() => InvokeBlocking(action, caller));
- }
-
- ///
- /// Invokes an action on the game thread and blocks until it is completed.
- ///
- ///
- [MethodImpl(MethodImplOptions.NoInlining)]
- public void InvokeBlocking(Action action, [CallerMemberName] string caller = "")
- {
- if (action == null)
- return;
-
- if (Thread.CurrentThread == MySandboxGame.Static.UpdateThread)
- {
- Debug.Assert(false, $"{nameof(InvokeBlocking)} should not be called on the game thread.");
- action.Invoke();
- return;
- }
-
- var e = new AutoResetEvent(false);
-
+ var ctx = new TaskCompletionSource();
MySandboxGame.Static.Invoke(() =>
{
try
{
action.Invoke();
+ ctx.SetResult(true);
+ }
+ catch (Exception e)
+ {
+ ctx.SetException(e);
}
finally
{
- e.Set();
+ Debug.Assert(ctx.Task.IsCompleted);
}
}, caller);
-
- if (!e.WaitOne(60000))
- throw new TimeoutException("The game action timed out.");
+ return ctx.Task;
}
#endregion
@@ -316,8 +350,8 @@ namespace Torch
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
- _game = new VRageGame(this, TweakGameSettings, SteamAppName, SteamAppId, Config.InstancePath, RunArgs);
- if (!_game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromMinutes(5)))
+ 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();
@@ -340,15 +374,15 @@ namespace Torch
public virtual void Destroy()
{
Managers.Detach();
- _game.SignalDestroy();
- if (!_game.WaitFor(VRageGame.GameState.Destroyed, TimeSpan.FromSeconds(15)))
+ Game.SignalDestroy();
+ if (!Game.WaitFor(VRageGame.GameState.Destroyed, TimeSpan.FromSeconds(15)))
Log.Warn("Failed to wait for the game to be destroyed");
- _game = null;
+ Game = null;
}
#endregion
- private VRageGame _game;
+ protected VRageGame Game { get; private set; }
///
/// Called after the basic game information is filled, but before the game is created.
@@ -390,8 +424,8 @@ namespace Torch
///
public virtual void Start()
{
- _game.SignalStart();
- if (!_game.WaitFor(VRageGame.GameState.Running, TimeSpan.FromSeconds(15)))
+ Game.SignalStart();
+ if (!Game.WaitFor(VRageGame.GameState.Running, TimeSpan.FromSeconds(15)))
Log.Warn("Failed to wait for the game to be started");
}
@@ -399,8 +433,8 @@ namespace Torch
public virtual void Stop()
{
LogManager.Flush();
- _game.SignalStop();
- if (!_game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromSeconds(15)))
+ 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
index 65fd80b..c0ac111 100644
--- a/Torch/VRageGame.cs
+++ b/Torch/VRageGame.cs
@@ -10,11 +10,15 @@ using Havok;
using NLog;
using NLog.Fluent;
using Sandbox;
+using Sandbox.Engine.Analytics;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Platform.VideoMode;
+using Sandbox.Engine.Utils;
using Sandbox.Game;
+using Sandbox.Game.Gui;
using Sandbox.Game.World;
+using Sandbox.Graphics.GUI;
using SpaceEngineers.Game;
using SpaceEngineers.Game.GUI;
using Torch.Utils;
@@ -223,15 +227,64 @@ namespace Torch
StateChange(GameState.Stopped);
}
}
-
- private void LoadSession(string sessionPath)
+
+ private void DoDisableAutoload()
{
- // ?
- MySessionLoader.LoadSingleplayerSession(sessionPath);
+ if (MySandboxGame.ConfigDedicated is MyConfigDedicated config)
+ {
+ var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ Directory.CreateDirectory(tempDirectory);
+ config.LoadWorld = null;
+ config.PremadeCheckpointPath = tempDirectory;
+ }
}
- private void UnloadSession()
+
+#pragma warning disable 649
+ [ReflectedMethod(Name = "StartServer")]
+ private static Action _hostServerForSession;
+#pragma warning restore 649
+
+ private void DoLoadSession(string sessionPath)
{
+ if (!Path.IsPathRooted(sessionPath))
+ sessionPath = Path.Combine(MyFileSystem.SavesPath, sessionPath);
+
+ if (!Sandbox.Engine.Platform.Game.IsDedicated)
+ {
+ MySessionLoader.LoadSingleplayerSession(sessionPath);
+ return;
+ }
+ ulong checkpointSize;
+ MyObjectBuilder_Checkpoint checkpoint = MyLocalCache.LoadCheckpoint(sessionPath, out checkpointSize);
+ if (MySession.IsCompatibleVersion(checkpoint))
+ {
+ if (MySteamWorkshop.DownloadWorldModsBlocking(checkpoint.Mods).Success)
+ {
+ // MySpaceAnalytics.Instance.SetEntry(MyGameEntryEnum.Load);
+ MySession.Load(sessionPath, checkpoint, checkpointSize);
+ _hostServerForSession(MySession.Static, MyMultiplayer.Static);
+ }
+ else
+ MyLog.Default.WriteLineAndConsole("Unable to download mods");
+ }
+ else
+ MyLog.Default.WriteLineAndConsole(MyTexts.Get(MyCommonTexts.DialogTextIncompatibleWorldVersion)
+ .ToString());
+ }
+
+ private void DoJoinSession(ulong lobbyId)
+ {
+ MyJoinGameHelper.JoinGame(lobbyId);
+ }
+
+ private void DoUnloadSession()
+ {
+ if (!Sandbox.Engine.Platform.Game.IsDedicated)
+ {
+ MyScreenManager.CloseAllScreensExcept(null);
+ MyGuiSandbox.Update(16);
+ }
if (MySession.Static != null)
{
MySession.Static.Unload();
@@ -250,6 +303,10 @@ namespace Torch
{
MyMultiplayer.Static.Dispose();
}
+ if (!Sandbox.Engine.Platform.Game.IsDedicated)
+ {
+ MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen(MyPerGameSettings.GUI.MainMenu));
+ }
}
private void DoStop()
@@ -286,6 +343,21 @@ namespace Torch
_commandChanged.Set();
}
+ public Task LoadSession(string path)
+ {
+ return _torch.InvokeAsync(()=>DoLoadSession(path));
+ }
+
+ public Task JoinSession(ulong lobbyId)
+ {
+ return _torch.InvokeAsync(()=>DoJoinSession(lobbyId));
+ }
+
+ public Task UnloadSession()
+ {
+ return _torch.InvokeAsync(DoUnloadSession);
+ }
+
///
/// Waits for the game to transition to the given state
///