Merge branch 'new_init'
This commit is contained in:
12
Jenkinsfile
vendored
12
Jenkinsfile
vendored
@@ -71,16 +71,4 @@ node {
|
|||||||
]]
|
]]
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.BRANCH_NAME == "master") {
|
|
||||||
gitVersion = bat(returnStdout: true, script: "@git describe --tags").trim()
|
|
||||||
gitSimpleVersion = bat(returnStdout: true, script: "@git describe --tags --abbrev=0").trim()
|
|
||||||
if (gitVersion == gitSimpleVersion) {
|
|
||||||
stage('${buildMode}') {
|
|
||||||
withCredentials([usernamePassword(credentialsId: 'torch-github', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
|
|
||||||
powershell "& ./Jenkins/${buildMode}.ps1 \"https://api.github.com/repos/TorchAPI/Torch/\" \"$gitSimpleVersion\" \"$USERNAME:$PASSWORD\" @(\"bin/torch-server.zip\", \"bin/torch-client.zip\")"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,11 +1,12 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
|
||||||
namespace Torch.API.Event
|
namespace Torch.API.Event
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manager class responsible for registration of event handlers.
|
/// Manager class responsible for registration of event handlers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IEventManager
|
public interface IEventManager : IManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers all event handler methods contained in the given instance
|
/// Registers all event handler methods contained in the given instance
|
||||||
|
@@ -80,12 +80,12 @@ namespace Torch.API
|
|||||||
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
|
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start the Torch instance.
|
/// Signals the torch instance to start, then blocks until it's started.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop the Torch instance.
|
/// Signals the torch instance to stop, then blocks until it's stopped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
@@ -101,10 +101,15 @@ namespace Torch.API
|
|||||||
Task Save(long callerId);
|
Task Save(long callerId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize the Torch instance.
|
/// Initialize the Torch instance. Before this <see cref="Start"/> is invalid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes the Torch instance. After this <see cref="Start"/> is invalid.
|
||||||
|
/// </summary>
|
||||||
|
void Dispose();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current state of the game this instance of torch is controlling.
|
/// The current state of the game this instance of torch is controlling.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -127,7 +127,6 @@
|
|||||||
<Compile Include="TorchClient.cs" />
|
<Compile Include="TorchClient.cs" />
|
||||||
<Compile Include="TorchClientConfig.cs" />
|
<Compile Include="TorchClientConfig.cs" />
|
||||||
<Compile Include="TorchConsoleScreen.cs" />
|
<Compile Include="TorchConsoleScreen.cs" />
|
||||||
<Compile Include="TorchMainMenuScreen.cs" />
|
|
||||||
<Compile Include="TorchSettingsScreen.cs" />
|
<Compile Include="TorchSettingsScreen.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
@@ -140,6 +139,9 @@
|
|||||||
<DependentUpon>Settings.settings</DependentUpon>
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="UI\TorchMainMenuScreen.cs" />
|
||||||
|
<Compile Include="UI\TorchNavScreen.cs" />
|
||||||
|
<Compile Include="UI\TorchSettingsScreen.cs" />
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
@@ -14,6 +14,7 @@ using Torch.API;
|
|||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.API.Session;
|
using Torch.API.Session;
|
||||||
using Torch.Client.Manager;
|
using Torch.Client.Manager;
|
||||||
|
using Torch.Client.UI;
|
||||||
using Torch.Session;
|
using Torch.Session;
|
||||||
using VRage;
|
using VRage;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
@@ -27,7 +28,9 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
private MyCommonProgramStartup _startup;
|
private MyCommonProgramStartup _startup;
|
||||||
private IMyRender _renderer;
|
private IMyRender _renderer;
|
||||||
private const uint APP_ID = 244850;
|
|
||||||
|
protected override uint SteamAppId => 244850;
|
||||||
|
protected override string SteamAppName => "Space Engineers";
|
||||||
|
|
||||||
public TorchClient()
|
public TorchClient()
|
||||||
{
|
{
|
||||||
@@ -46,10 +49,9 @@ namespace Torch.Client
|
|||||||
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
|
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
|
||||||
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
|
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
|
||||||
Log.Info("Initializing Torch Client");
|
Log.Info("Initializing Torch Client");
|
||||||
base.Init();
|
|
||||||
|
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
|
||||||
_startup = new MyCommonProgramStartup(RunArgs);
|
_startup = new MyCommonProgramStartup(RunArgs);
|
||||||
|
SpaceEngineersGame.SetupBasicGameInfo();
|
||||||
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
if (_startup.PerformReporting())
|
if (_startup.PerformReporting())
|
||||||
throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
|
throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
|
||||||
|
|
||||||
@@ -58,27 +60,16 @@ namespace Torch.Client
|
|||||||
throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
|
throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
|
||||||
|
|
||||||
var appDataPath = _startup.GetAppDataPath();
|
var appDataPath = _startup.GetAppDataPath();
|
||||||
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
|
Config.InstancePath = appDataPath;
|
||||||
MyInitializer.InitCheckSum();
|
base.Init();
|
||||||
_startup.InitSplashScreen();
|
OverrideMenus();
|
||||||
if (!_startup.Check64Bit())
|
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
||||||
throw new InvalidOperationException("Torch requires a 64bit operating system");
|
}
|
||||||
|
|
||||||
_startup.DetectSharpDxLeaksBeforeRun();
|
public override void Dispose()
|
||||||
var steamService = new SteamService(Game.IsDedicated, APP_ID);
|
{
|
||||||
MyServiceManager.Instance.AddService<IMyGameService>(steamService);
|
base.Dispose();
|
||||||
_renderer = null;
|
_startup.DetectSharpDxLeaksAfterRun();
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
|
||||||
// I'm sorry, but it's what Keen does in SpaceEngineers.MyProgram
|
|
||||||
#pragma warning disable 612
|
|
||||||
SpaceEngineersGame.SetupRender();
|
|
||||||
#pragma warning restore 612
|
|
||||||
InitializeRender();
|
|
||||||
if (!_startup.CheckSteamRunning())
|
|
||||||
throw new InvalidOperationException("Space Engineers requires steam to be running");
|
|
||||||
|
|
||||||
if (!Game.IsDedicated)
|
|
||||||
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OverrideMenus()
|
private void OverrideMenus()
|
||||||
@@ -96,18 +87,6 @@ namespace Torch.Client
|
|||||||
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Start()
|
|
||||||
{
|
|
||||||
using (var spaceEngineersGame = new SpaceEngineersGame(RunArgs))
|
|
||||||
{
|
|
||||||
Log.Info("Starting client");
|
|
||||||
OverrideMenus();
|
|
||||||
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded;
|
|
||||||
spaceEngineersGame.OnGameExit += Dispose;
|
|
||||||
spaceEngineersGame.Run(false, _startup.DisposeSplashScreen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetRenderWindowTitle(string title)
|
private void SetRenderWindowTitle(string title)
|
||||||
{
|
{
|
||||||
MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
|
MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
|
||||||
@@ -125,58 +104,9 @@ namespace Torch.Client
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e)
|
public override void Restart()
|
||||||
{
|
{
|
||||||
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
throw new NotImplementedException();
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispose()
|
|
||||||
{
|
|
||||||
MyGameService.ShutDown();
|
|
||||||
_startup.DetectSharpDxLeaksAfterRun();
|
|
||||||
MyInitializer.InvokeAfterRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Stop()
|
|
||||||
{
|
|
||||||
MySandboxGame.ExitThreadSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeRender()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Game.IsDedicated)
|
|
||||||
{
|
|
||||||
_renderer = new MyNullRender();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var graphicsRenderer = MySandboxGame.Config.GraphicsRenderer;
|
|
||||||
if (graphicsRenderer == MySandboxGame.DirectX11RendererKey)
|
|
||||||
{
|
|
||||||
_renderer = new MyDX11Render();
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
MessageBox.Show(ex.Message, "Render Initialization Failed");
|
|
||||||
Environment.Exit(-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
#pragma warning disable 618
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Sandbox.Game.Gui;
|
|
||||||
using Sandbox.Graphics;
|
|
||||||
using Sandbox.Graphics.GUI;
|
|
||||||
using Sandbox.Gui;
|
|
||||||
using SpaceEngineers.Game.GUI;
|
|
||||||
using VRage.Game;
|
|
||||||
using VRage.Utils;
|
|
||||||
using VRageMath;
|
|
||||||
|
|
||||||
namespace Torch.Client
|
|
||||||
{
|
|
||||||
public class TorchMainMenuScreen : MyGuiScreenMainMenu
|
|
||||||
{
|
|
||||||
public TorchMainMenuScreen()
|
|
||||||
: this(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public TorchMainMenuScreen(bool pauseGame)
|
|
||||||
: base(pauseGame)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void RecreateControls(bool constructor)
|
|
||||||
{
|
|
||||||
base.RecreateControls(constructor);
|
|
||||||
|
|
||||||
var buttonSize = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
|
|
||||||
Vector2 leftButtonPositionOrigin = MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_BOTTOM) + new Vector2(buttonSize.X / 2f, 0f);
|
|
||||||
var btn = MakeButton(leftButtonPositionOrigin - 9 * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA, MyStringId.GetOrCompute("Torch"), TorchButtonClicked);
|
|
||||||
Controls.Add(btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TorchButtonClicked(MyGuiControlButton obj)
|
|
||||||
{
|
|
||||||
MyGuiSandbox.AddScreen(new TorchSettingsScreen());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
56
Torch.Client/UI/TorchMainMenuScreen.cs
Normal file
56
Torch.Client/UI/TorchMainMenuScreen.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma warning disable 618
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Engine.Utils;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Graphics;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using Sandbox.Gui;
|
||||||
|
using SpaceEngineers.Game.GUI;
|
||||||
|
using Torch.Utils;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace Torch.Client.UI
|
||||||
|
{
|
||||||
|
public class TorchMainMenuScreen : MyGuiScreenMainMenu
|
||||||
|
{
|
||||||
|
#pragma warning disable 169
|
||||||
|
[ReflectedGetter(Name = "m_elementGroup")]
|
||||||
|
private static Func<MyGuiScreenMainMenu, MyGuiControlElementGroup> _elementsGroup;
|
||||||
|
#pragma warning restore 169
|
||||||
|
|
||||||
|
public TorchMainMenuScreen() : this(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorchMainMenuScreen(bool pauseGame)
|
||||||
|
: base(pauseGame)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void RecreateControls(bool constructor)
|
||||||
|
{
|
||||||
|
base.RecreateControls(constructor);
|
||||||
|
|
||||||
|
Vector2 minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
|
||||||
|
Vector2 value = MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_BOTTOM, 54, 54) + new Vector2(minSizeGui.X / 2f, 0f) + new Vector2(15f, 0f) / MyGuiConstants.GUI_OPTIMAL_SIZE;
|
||||||
|
|
||||||
|
MyGuiControlButton myGuiControlButton = MakeButton(value - 9 * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA,
|
||||||
|
MyStringId.GetOrCompute("Torch"), TorchButtonClicked, MyCommonTexts.ToolTipExitToWindows);
|
||||||
|
Controls.Add(myGuiControlButton);
|
||||||
|
_elementsGroup.Invoke(this).Add(myGuiControlButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TorchButtonClicked(MyGuiControlButton obj)
|
||||||
|
{
|
||||||
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen<TorchNavScreen>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
Torch.Client/UI/TorchNavScreen.cs
Normal file
49
Torch.Client/UI/TorchNavScreen.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Sandbox;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace Torch.Client.UI
|
||||||
|
{
|
||||||
|
public class TorchNavScreen : MyGuiScreenBase
|
||||||
|
{
|
||||||
|
private MyGuiControlElementGroup _elementGroup;
|
||||||
|
|
||||||
|
public TorchNavScreen() : base(new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR, new Vector2(0.35875f, 0.558333337f))
|
||||||
|
{
|
||||||
|
EnabledBackgroundFade = true;
|
||||||
|
RecreateControls(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RecreateControls(bool constructor)
|
||||||
|
{
|
||||||
|
base.RecreateControls(constructor);
|
||||||
|
_elementGroup = new MyGuiControlElementGroup();
|
||||||
|
_elementGroup.HighlightChanged += ElementGroupHighlightChanged;
|
||||||
|
AddCaption(MyCommonTexts.ScreenCaptionOptions, null, null);
|
||||||
|
var value = new Vector2(0f, -m_size.Value.Y / 2f + 0.146f);
|
||||||
|
var num = 0;
|
||||||
|
var myGuiControlButton = new MyGuiControlButton(value + num++ * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER, null, MyTexts.Get(MyCommonTexts.ScreenOptionsButtonGame), 0.8f, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER, MyGuiControlHighlightType.WHEN_ACTIVE, delegate(MyGuiControlButton sender)
|
||||||
|
{
|
||||||
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen<TorchSettingsScreen>());
|
||||||
|
}, GuiSounds.MouseClick, 1f, null);
|
||||||
|
Controls.Add(myGuiControlButton);
|
||||||
|
_elementGroup.Add(myGuiControlButton);
|
||||||
|
CloseButtonEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ElementGroupHighlightChanged(MyGuiControlElementGroup obj)
|
||||||
|
{
|
||||||
|
foreach (MyGuiControlBase current in _elementGroup)
|
||||||
|
if (current.HasFocus && obj.SelectedElement != current)
|
||||||
|
FocusedControl = obj.SelectedElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetFriendlyName() => "Torch";
|
||||||
|
|
||||||
|
public void OnBackClick(MyGuiControlButton sender) => CloseScreen();
|
||||||
|
}
|
||||||
|
}
|
25
Torch.Client/UI/TorchSettingsScreen.cs
Normal file
25
Torch.Client/UI/TorchSettingsScreen.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace Torch.Client.UI
|
||||||
|
{
|
||||||
|
public class TorchSettingsScreen : MyGuiScreenBase
|
||||||
|
{
|
||||||
|
public TorchSettingsScreen() : base(new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR,
|
||||||
|
new Vector2(0.35875f, 0.558333337f))
|
||||||
|
{
|
||||||
|
EnabledBackgroundFade = true;
|
||||||
|
RecreateControls(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string GetFriendlyName() => "Torch Settings";
|
||||||
|
|
||||||
|
public void OnBackClick(MyGuiControlButton sender) => CloseScreen();
|
||||||
|
}
|
||||||
|
}
|
@@ -90,7 +90,7 @@ quit";
|
|||||||
{
|
{
|
||||||
var ui = new TorchUI(_server);
|
var ui = new TorchUI(_server);
|
||||||
if (_config.Autostart)
|
if (_config.Autostart)
|
||||||
new Thread(_server.Start).Start();
|
_server.Start();
|
||||||
ui.ShowDialog();
|
ui.ShowDialog();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -33,22 +33,12 @@ namespace Torch.Server.Managers
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Attach()
|
|
||||||
{
|
|
||||||
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
|
|
||||||
MyFileSystem.Init("Content", Torch.Config.InstancePath);
|
|
||||||
//Initializes saves path. Why this isn't in Init() we may never know.
|
|
||||||
MyFileSystem.InitUserSpecific(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadInstance(string path, bool validate = true)
|
public void LoadInstance(string path, bool validate = true)
|
||||||
{
|
{
|
||||||
if (validate)
|
if (validate)
|
||||||
ValidateInstance(path);
|
ValidateInstance(path);
|
||||||
|
|
||||||
MyFileSystem.Reset();
|
MyFileSystem.Reset();
|
||||||
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
|
|
||||||
MyFileSystem.Init("Content", path);
|
MyFileSystem.Init("Content", path);
|
||||||
//Initializes saves path. Why this isn't in Init() we may never know.
|
//Initializes saves path. Why this isn't in Init() we may never know.
|
||||||
MyFileSystem.InitUserSpecific(null);
|
MyFileSystem.InitUserSpecific(null);
|
||||||
|
@@ -25,8 +25,7 @@ namespace Torch.Server.Managers
|
|||||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[ReflectedGetter(Name = "m_members")]
|
[ReflectedGetter(Name = "m_members")] private static Func<MyDedicatedServerBase, List<ulong>> _members;
|
||||||
private static Func<MyDedicatedServerBase, List<ulong>> _members;
|
|
||||||
[ReflectedGetter(Name = "m_waitingForGroup")]
|
[ReflectedGetter(Name = "m_waitingForGroup")]
|
||||||
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
|
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
@@ -37,7 +36,9 @@ namespace Torch.Server.Managers
|
|||||||
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
|
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MultiplayerManagerDedicated(ITorchBase torch) : base(torch) { }
|
public MultiplayerManagerDedicated(ITorchBase torch) : base(torch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
|
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
|
||||||
@@ -54,7 +55,8 @@ namespace Torch.Server.Managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) || MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
|
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Attach()
|
public override void Attach()
|
||||||
@@ -62,8 +64,10 @@ namespace Torch.Server.Managers
|
|||||||
base.Attach();
|
base.Attach();
|
||||||
_gameServerValidateAuthTicketReplacer = _gameServerValidateAuthTicketFactory.Invoke();
|
_gameServerValidateAuthTicketReplacer = _gameServerValidateAuthTicketFactory.Invoke();
|
||||||
_gameServerUserGroupStatusReplacer = _gameServerUserGroupStatusFactory.Invoke();
|
_gameServerUserGroupStatusReplacer = _gameServerUserGroupStatusFactory.Invoke();
|
||||||
_gameServerValidateAuthTicketReplacer.Replace(new Action<ulong, JoinResult, ulong>(ValidateAuthTicketResponse), MyGameService.GameServer);
|
_gameServerValidateAuthTicketReplacer.Replace(
|
||||||
_gameServerUserGroupStatusReplacer.Replace(new Action<ulong, ulong, bool, bool>(UserGroupStatusResponse), MyGameService.GameServer);
|
new Action<ulong, JoinResult, ulong>(ValidateAuthTicketResponse), MyGameService.GameServer);
|
||||||
|
_gameServerUserGroupStatusReplacer.Replace(new Action<ulong, ulong, bool, bool>(UserGroupStatusResponse),
|
||||||
|
MyGameService.GameServer);
|
||||||
_log.Info("Inserted steam authentication intercept");
|
_log.Info("Inserted steam authentication intercept");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,15 +84,20 @@ namespace Torch.Server.Managers
|
|||||||
|
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.ValidateAuthTicketResponse), typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
|
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.ValidateAuthTicketResponse),
|
||||||
|
typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
|
||||||
private static Func<ReflectedEventReplacer> _gameServerValidateAuthTicketFactory;
|
private static Func<ReflectedEventReplacer> _gameServerValidateAuthTicketFactory;
|
||||||
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.UserGroupStatusResponse), typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
|
|
||||||
|
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.UserGroupStatusResponse),
|
||||||
|
typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
|
||||||
private static Func<ReflectedEventReplacer> _gameServerUserGroupStatusFactory;
|
private static Func<ReflectedEventReplacer> _gameServerUserGroupStatusFactory;
|
||||||
|
|
||||||
private ReflectedEventReplacer _gameServerValidateAuthTicketReplacer;
|
private ReflectedEventReplacer _gameServerValidateAuthTicketReplacer;
|
||||||
private ReflectedEventReplacer _gameServerUserGroupStatusReplacer;
|
private ReflectedEventReplacer _gameServerUserGroupStatusReplacer;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
#region CustomAuth
|
#region CustomAuth
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
[ReflectedStaticMethod(Type = typeof(MyDedicatedServerBase), Name = "ConvertSteamIDFrom64")]
|
[ReflectedStaticMethod(Type = typeof(MyDedicatedServerBase), Name = "ConvertSteamIDFrom64")]
|
||||||
private static Func<ulong, string> _convertSteamIDFrom64;
|
private static Func<ulong, string> _convertSteamIDFrom64;
|
||||||
@@ -96,78 +105,130 @@ namespace Torch.Server.Managers
|
|||||||
[ReflectedStaticMethod(Type = typeof(MyGameService), Name = "GetServerAccountType")]
|
[ReflectedStaticMethod(Type = typeof(MyGameService), Name = "GetServerAccountType")]
|
||||||
private static Func<ulong, MyGameServiceAccountType> _getServerAccountType;
|
private static Func<ulong, MyGameServiceAccountType> _getServerAccountType;
|
||||||
|
|
||||||
[ReflectedMethod(Name = "UserAccepted")]
|
[ReflectedMethod(Name = "UserAccepted")] private static Action<MyDedicatedServerBase, ulong> _userAcceptedImpl;
|
||||||
private static Action<MyDedicatedServerBase, ulong> _userAcceptedImpl;
|
|
||||||
|
|
||||||
[ReflectedMethod(Name = "UserRejected")]
|
[ReflectedMethod(Name = "UserRejected")]
|
||||||
private static Action<MyDedicatedServerBase, ulong, JoinResult> _userRejected;
|
private static Action<MyDedicatedServerBase, ulong, JoinResult> _userRejected;
|
||||||
[ReflectedMethod(Name = "IsClientBanned")]
|
|
||||||
private static Func<MyMultiplayerBase, ulong, bool> _isClientBanned;
|
[ReflectedMethod(Name = "IsClientBanned")] private static Func<MyMultiplayerBase, ulong, bool> _isClientBanned;
|
||||||
[ReflectedMethod(Name = "IsClientKicked")]
|
[ReflectedMethod(Name = "IsClientKicked")] private static Func<MyMultiplayerBase, ulong, bool> _isClientKicked;
|
||||||
private static Func<MyMultiplayerBase, ulong, bool> _isClientKicked;
|
|
||||||
[ReflectedMethod(Name = "RaiseClientKicked")]
|
[ReflectedMethod(Name = "RaiseClientKicked")]
|
||||||
private static Action<MyMultiplayerBase, ulong> _raiseClientKicked;
|
private static Action<MyMultiplayerBase, ulong> _raiseClientKicked;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private const int _waitListSize = 32;
|
||||||
|
private readonly List<WaitingForGroup> _waitingForGroupLocal = new List<WaitingForGroup>(_waitListSize);
|
||||||
|
|
||||||
|
private struct WaitingForGroup
|
||||||
|
{
|
||||||
|
public readonly ulong SteamId;
|
||||||
|
public readonly JoinResult Response;
|
||||||
|
public readonly ulong SteamOwner;
|
||||||
|
|
||||||
|
public WaitingForGroup(ulong id, JoinResult response, ulong owner)
|
||||||
|
{
|
||||||
|
SteamId = id;
|
||||||
|
Response = response;
|
||||||
|
SteamOwner = owner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Largely copied from SE
|
//Largely copied from SE
|
||||||
private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner)
|
private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner)
|
||||||
{
|
{
|
||||||
_log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
|
_log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
|
||||||
if (IsBanned(steamOwner))
|
if (MySandboxGame.ConfigDedicated.GroupID == 0uL)
|
||||||
|
RunEvent(new ValidateAuthTicketEvent(steamID, steamOwner, response, 0, true, false));
|
||||||
|
else if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
|
||||||
|
UserRejected(steamID, JoinResult.GroupIdInvalid);
|
||||||
|
else if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
|
||||||
|
lock (_waitingForGroupLocal)
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.BannedByAdmins);
|
if (_waitingForGroupLocal.Count >= _waitListSize)
|
||||||
_raiseClientKicked.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID);
|
_waitingForGroupLocal.RemoveAt(0);
|
||||||
|
_waitingForGroupLocal.Add(new WaitingForGroup(steamID, response, steamOwner));
|
||||||
}
|
}
|
||||||
else if (_isClientKicked.Invoke(MyMultiplayer.Static, steamOwner))
|
else
|
||||||
{
|
UserRejected(steamID, JoinResult.SteamServersOffline);
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.KickedRecently);
|
|
||||||
_raiseClientKicked.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID);
|
|
||||||
}
|
}
|
||||||
if (response != JoinResult.OK)
|
|
||||||
|
private void RunEvent(ValidateAuthTicketEvent info)
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, response);
|
MultiplayerManagerDedicatedEventShim.RaiseValidateAuthTicket(ref info);
|
||||||
|
|
||||||
|
if (info.FutureVerdict == null)
|
||||||
|
{
|
||||||
|
if (IsBanned(info.SteamOwner) || IsBanned(info.SteamID))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.BannedByAdmins);
|
||||||
|
else if (_isClientKicked(MyMultiplayer.Static, info.SteamID) ||
|
||||||
|
_isClientKicked(MyMultiplayer.Static, info.SteamOwner))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.KickedRecently);
|
||||||
|
else if (info.SteamResponse != JoinResult.OK)
|
||||||
|
CommitVerdict(info.SteamID, info.SteamResponse);
|
||||||
|
else if (MyMultiplayer.Static.MemberLimit > 0 &&
|
||||||
|
MyMultiplayer.Static.MemberCount + 1 > MyMultiplayer.Static.MemberLimit)
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.ServerFull);
|
||||||
|
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString()) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(_convertSteamIDFrom64(info.SteamID)))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.OK);
|
||||||
|
else if (MySandboxGame.ConfigDedicated.GroupID == info.Group && (info.Member || info.Officer))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.OK);
|
||||||
|
else
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.NotInGroup);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MyMultiplayer.Static.MemberLimit > 0 && _members.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Count - 1 >= MyMultiplayer.Static.MemberLimit)
|
|
||||||
|
info.FutureVerdict.ContinueWith((task) =>
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.ServerFull);
|
JoinResult verdict;
|
||||||
return;
|
if (task.IsFaulted)
|
||||||
}
|
|
||||||
if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
|
|
||||||
MySandboxGame.ConfigDedicated.Administrators.Contains(steamID.ToString()) ||
|
|
||||||
MySandboxGame.ConfigDedicated.Administrators.Contains(_convertSteamIDFrom64(steamID)))
|
|
||||||
{
|
{
|
||||||
this.UserAccepted(steamID);
|
_log.Error(task.Exception, $"Future validation verdict faulted");
|
||||||
return;
|
verdict = JoinResult.TicketCanceled;
|
||||||
}
|
}
|
||||||
if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
|
else
|
||||||
|
verdict = task.Result;
|
||||||
|
Torch.Invoke(() => { CommitVerdict(info.SteamID, verdict); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CommitVerdict(ulong steamId, JoinResult verdict)
|
||||||
{
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.GroupIdInvalid);
|
if (verdict == JoinResult.OK)
|
||||||
return;
|
UserAccepted(steamId);
|
||||||
}
|
else
|
||||||
if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
|
UserRejected(steamId, verdict);
|
||||||
{
|
|
||||||
_waitingForGroup.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Add(steamID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamID, JoinResult.SteamServersOffline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserGroupStatusResponse(ulong userId, ulong groupId, bool member, bool officer)
|
private void UserGroupStatusResponse(ulong userId, ulong groupId, bool member, bool officer)
|
||||||
{
|
{
|
||||||
if (groupId == MySandboxGame.ConfigDedicated.GroupID && _waitingForGroup.Invoke((MyDedicatedServerBase)MyMultiplayer.Static).Remove(userId))
|
lock (_waitingForGroupLocal)
|
||||||
|
for (var j = 0; j < _waitingForGroupLocal.Count; j++)
|
||||||
{
|
{
|
||||||
if (member || officer)
|
var wait = _waitingForGroupLocal[j];
|
||||||
UserAccepted(userId);
|
if (wait.SteamId == userId)
|
||||||
else
|
{
|
||||||
_userRejected.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, userId, JoinResult.NotInGroup);
|
RunEvent(new ValidateAuthTicketEvent(wait.SteamId, wait.SteamOwner, wait.Response, groupId,
|
||||||
|
member, officer));
|
||||||
|
_waitingForGroupLocal.RemoveAt(j);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserRejected(ulong steamId, JoinResult reason)
|
||||||
|
{
|
||||||
|
_userRejected.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId, reason);
|
||||||
|
}
|
||||||
|
|
||||||
private void UserAccepted(ulong steamId)
|
private void UserAccepted(ulong steamId)
|
||||||
{
|
{
|
||||||
_userAcceptedImpl.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId);
|
_userAcceptedImpl.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId);
|
||||||
base.RaiseClientJoined(steamId);
|
base.RaiseClientJoined(steamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Sandbox;
|
||||||
|
using Torch.API.Event;
|
||||||
|
using Torch.Event;
|
||||||
|
using VRage.Network;
|
||||||
|
|
||||||
|
namespace Torch.Server.Managers
|
||||||
|
{
|
||||||
|
[EventShim]
|
||||||
|
internal static class MultiplayerManagerDedicatedEventShim
|
||||||
|
{
|
||||||
|
private static readonly EventList<ValidateAuthTicketEvent> _eventValidateAuthTicket =
|
||||||
|
new EventList<ValidateAuthTicketEvent>();
|
||||||
|
|
||||||
|
|
||||||
|
internal static void RaiseValidateAuthTicket(ref ValidateAuthTicketEvent info)
|
||||||
|
{
|
||||||
|
_eventValidateAuthTicket?.RaiseEvent(ref info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that occurs when a player tries to connect to a dedicated server.
|
||||||
|
/// Use these values to choose a <see cref="ValidateAuthTicketEvent.FutureVerdict"/>,
|
||||||
|
/// or leave it unset to allow the default logic to handle the request.
|
||||||
|
/// </summary>
|
||||||
|
public struct ValidateAuthTicketEvent : IEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SteamID of the player
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong SteamID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SteamID of the game owner
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong SteamOwner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The response from steam
|
||||||
|
/// </summary>
|
||||||
|
public readonly JoinResult SteamResponse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID of the queried group, or <c>0</c> if no group.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ulong Group;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this person a member of <see cref="Group"/>. If no group this is true.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Member;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this person an officer of <see cref="Group"/>. If no group this is false.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Officer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A future verdict on this authorization request. If null, let the default logic choose. If not async use <see cref="Task.FromResult{TResult}(TResult)"/>
|
||||||
|
/// </summary>
|
||||||
|
public Task<JoinResult> FutureVerdict;
|
||||||
|
|
||||||
|
internal ValidateAuthTicketEvent(ulong steamId, ulong steamOwner, JoinResult steamResponse,
|
||||||
|
ulong serverGroup, bool member, bool officer)
|
||||||
|
{
|
||||||
|
SteamID = steamId;
|
||||||
|
SteamOwner = steamOwner;
|
||||||
|
SteamResponse = steamResponse;
|
||||||
|
Group = serverGroup;
|
||||||
|
Member = member;
|
||||||
|
Officer = officer;
|
||||||
|
FutureVerdict = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Cancelled => FutureVerdict != null;
|
||||||
|
}
|
||||||
|
}
|
@@ -198,6 +198,7 @@
|
|||||||
<Compile Include="Managers\EntityControlManager.cs" />
|
<Compile Include="Managers\EntityControlManager.cs" />
|
||||||
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
||||||
<Compile Include="Managers\InstanceManager.cs" />
|
<Compile Include="Managers\InstanceManager.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicatedEventShim.cs" />
|
||||||
<Compile Include="NativeMethods.cs" />
|
<Compile Include="NativeMethods.cs" />
|
||||||
<Compile Include="Initializer.cs" />
|
<Compile Include="Initializer.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@@ -44,19 +44,58 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public float SimulationRatio { get => _simRatio; set { _simRatio = value; OnPropertyChanged(); } }
|
public float SimulationRatio
|
||||||
|
{
|
||||||
|
get => _simRatio;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_simRatio = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } }
|
public TimeSpan ElapsedPlayTime
|
||||||
|
{
|
||||||
|
get => _elapsedPlayTime;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_elapsedPlayTime = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Thread GameThread { get; private set; }
|
public Thread GameThread { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
|
public ServerState State
|
||||||
|
{
|
||||||
|
get => _state;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_state = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsRunning { get => _isRunning; set { _isRunning = value; OnPropertyChanged(); } }
|
public bool IsRunning
|
||||||
|
{
|
||||||
|
get => _isRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isRunning = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public InstanceManager DedicatedInstance { get; }
|
public InstanceManager DedicatedInstance { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string InstanceName => Config?.InstanceName;
|
public string InstanceName => Config?.InstanceName;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string InstancePath => Config?.InstancePath;
|
public string InstancePath => Config?.InstancePath;
|
||||||
|
|
||||||
@@ -64,7 +103,6 @@ namespace Torch.Server
|
|||||||
private ServerState _state;
|
private ServerState _state;
|
||||||
private TimeSpan _elapsedPlayTime;
|
private TimeSpan _elapsedPlayTime;
|
||||||
private float _simRatio;
|
private float _simRatio;
|
||||||
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
|
||||||
private Timer _watchdog;
|
private Timer _watchdog;
|
||||||
private Stopwatch _uptime;
|
private Stopwatch _uptime;
|
||||||
|
|
||||||
@@ -80,30 +118,20 @@ namespace Torch.Server
|
|||||||
sessionManager.AddFactory((x) => new MultiplayerManagerDedicated(this));
|
sessionManager.AddFactory((x) => new MultiplayerManagerDedicated(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override uint SteamAppId => 244850;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string SteamAppName => "SpaceEngineersDedicated";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
||||||
|
Sandbox.Engine.Platform.Game.IsDedicated = true;
|
||||||
|
|
||||||
base.Init();
|
base.Init();
|
||||||
|
|
||||||
MyPerGameSettings.SendLogToKeen = false;
|
|
||||||
MyPerServerSettings.GameName = MyPerGameSettings.GameName;
|
|
||||||
MyPerServerSettings.GameNameSafe = MyPerGameSettings.GameNameSafe;
|
|
||||||
MyPerServerSettings.GameDSName = MyPerServerSettings.GameNameSafe + "Dedicated";
|
|
||||||
MyPerServerSettings.GameDSDescription = "Your place for space engineering, destruction and exploring.";
|
|
||||||
MySessionComponentExtDebug.ForceDisable = true;
|
|
||||||
MyPerServerSettings.AppId = 244850;
|
|
||||||
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
|
|
||||||
InvokeBeforeRun();
|
|
||||||
|
|
||||||
//MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
|
|
||||||
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
|
||||||
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
|
|
||||||
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
|
|
||||||
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
|
|
||||||
MyPlugins.Load();
|
|
||||||
MyGlobalTypeMetadata.Static.Init();
|
|
||||||
|
|
||||||
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
|
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
|
||||||
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
||||||
}
|
}
|
||||||
@@ -117,81 +145,65 @@ namespace Torch.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InvokeBeforeRun()
|
|
||||||
{
|
|
||||||
MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING);
|
|
||||||
MySandboxGame.Log.WriteLine("Steam build: Always true");
|
|
||||||
MySandboxGame.Log.WriteLine("Environment.ProcessorCount: " + MyEnvironment.ProcessorCount);
|
|
||||||
//MySandboxGame.Log.WriteLine("Environment.OSVersion: " + GetOsName());
|
|
||||||
MySandboxGame.Log.WriteLine("Environment.CommandLine: " + Environment.CommandLine);
|
|
||||||
MySandboxGame.Log.WriteLine("Environment.Is64BitProcess: " + MyEnvironment.Is64BitProcess);
|
|
||||||
MySandboxGame.Log.WriteLine("Environment.Is64BitOperatingSystem: " + Environment.Is64BitOperatingSystem);
|
|
||||||
//MySandboxGame.Log.WriteLine("Environment.Version: " + GetNETFromRegistry());
|
|
||||||
MySandboxGame.Log.WriteLine("Environment.CurrentDirectory: " + Environment.CurrentDirectory);
|
|
||||||
MySandboxGame.Log.WriteLine("MainAssembly.ProcessorArchitecture: " + Assembly.GetExecutingAssembly().GetArchitecture());
|
|
||||||
MySandboxGame.Log.WriteLine("ExecutingAssembly.ProcessorArchitecture: " + MyFileSystem.MainAssembly.GetArchitecture());
|
|
||||||
MySandboxGame.Log.WriteLine("IntPtr.Size: " + IntPtr.Size);
|
|
||||||
MySandboxGame.Log.WriteLine("Default Culture: " + CultureInfo.CurrentCulture.Name);
|
|
||||||
MySandboxGame.Log.WriteLine("Default UI Culture: " + CultureInfo.CurrentUICulture.Name);
|
|
||||||
MySandboxGame.Log.WriteLine("IsAdmin: " + new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator));
|
|
||||||
|
|
||||||
MyLog.Default = MySandboxGame.Log;
|
|
||||||
|
|
||||||
Thread.CurrentThread.Name = "Main thread";
|
|
||||||
|
|
||||||
//Because we want exceptions from users to be in english
|
|
||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
|
||||||
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
|
|
||||||
|
|
||||||
MySandboxGame.Config = new MyConfig("SpaceEngineers-Dedicated.cfg");
|
|
||||||
MySandboxGame.Config.Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ReflectedStaticMethod(Type = typeof(DedicatedServer), Name = "RunInternal")]
|
|
||||||
private static Action _dsRunInternal;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
if (State != ServerState.Stopped)
|
if (State != ServerState.Stopped)
|
||||||
return;
|
return;
|
||||||
|
State = ServerState.Starting;
|
||||||
|
IsRunning = true;
|
||||||
|
Log.Info("Starting server.");
|
||||||
|
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
|
||||||
|
|
||||||
DedicatedInstance.SaveConfig();
|
DedicatedInstance.SaveConfig();
|
||||||
_uptime = Stopwatch.StartNew();
|
_uptime = Stopwatch.StartNew();
|
||||||
IsRunning = true;
|
|
||||||
GameThread = Thread.CurrentThread;
|
|
||||||
State = ServerState.Starting;
|
|
||||||
Log.Info("Starting server.");
|
|
||||||
|
|
||||||
MySandboxGame.IsDedicated = true;
|
|
||||||
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
|
|
||||||
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
|
|
||||||
|
|
||||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
|
||||||
|
|
||||||
base.Start();
|
base.Start();
|
||||||
// Stops RunInternal from calling MyFileSystem.InitUserSpecific(null), we call it in InstanceManager.
|
|
||||||
MySandboxGame.IsReloading = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_dsRunInternal.Invoke();
|
|
||||||
}
|
|
||||||
catch (TargetInvocationException e)
|
|
||||||
{
|
|
||||||
// Makes log formatting a little nicer.
|
|
||||||
throw e.InnerException ?? e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MySandboxGame.Log.Close();
|
/// <inheritdoc />
|
||||||
|
public override void Stop()
|
||||||
|
{
|
||||||
|
if (State == ServerState.Stopped)
|
||||||
|
Log.Error("Server is already stopped");
|
||||||
|
Log.Info("Stopping server.");
|
||||||
|
base.Stop();
|
||||||
|
Log.Info("Server stopped.");
|
||||||
|
|
||||||
State = ServerState.Stopped;
|
State = ServerState.Stopped;
|
||||||
|
IsRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restart the program.
|
||||||
|
/// </summary>
|
||||||
|
public override void Restart()
|
||||||
|
{
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Init(object gameInstance)
|
public override void Init(object gameInstance)
|
||||||
{
|
{
|
||||||
base.Init(gameInstance);
|
base.Init(gameInstance);
|
||||||
|
var game = gameInstance as MySandboxGame;
|
||||||
|
if (game != null && MySession.Static != null)
|
||||||
|
{
|
||||||
State = ServerState.Running;
|
State = ServerState.Running;
|
||||||
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
// SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State = ServerState.Stopped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -205,10 +217,13 @@ namespace Torch.Server
|
|||||||
if (_watchdog == null && Config.TickTimeout > 0)
|
if (_watchdog == null && Config.TickTimeout > 0)
|
||||||
{
|
{
|
||||||
Log.Info("Starting server watchdog.");
|
Log.Info("Starting server watchdog.");
|
||||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(Config.TickTimeout));
|
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero,
|
||||||
|
TimeSpan.FromSeconds(Config.TickTimeout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Freeze Detection
|
||||||
|
|
||||||
private static void CheckServerResponding(object state)
|
private static void CheckServerResponding(object state)
|
||||||
{
|
{
|
||||||
var mre = new ManualResetEvent(false);
|
var mre = new ManualResetEvent(false);
|
||||||
@@ -216,7 +231,8 @@ namespace Torch.Server
|
|||||||
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Log.Error($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.");
|
Log.Error(
|
||||||
|
$"Server watchdog detected that the server was frozen for at least {((TorchServer) state).Config.TickTimeout} seconds.");
|
||||||
Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread));
|
Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread));
|
||||||
#else
|
#else
|
||||||
Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread));
|
Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread));
|
||||||
@@ -278,47 +294,7 @@ namespace Torch.Server
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
#endregion
|
||||||
public override void Stop()
|
|
||||||
{
|
|
||||||
if (State == ServerState.Stopped)
|
|
||||||
Log.Error("Server is already stopped");
|
|
||||||
|
|
||||||
if (Thread.CurrentThread != MySandboxGame.Static.UpdateThread)
|
|
||||||
{
|
|
||||||
Log.Debug("Invoking server stop on game thread.");
|
|
||||||
Invoke(Stop);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Info("Stopping server.");
|
|
||||||
|
|
||||||
//Unload all the static junk.
|
|
||||||
//TODO: Finish unloading all server data so it's in a completely clean state.
|
|
||||||
MySandboxGame.Static.Exit();
|
|
||||||
|
|
||||||
Log.Info("Server stopped.");
|
|
||||||
LogManager.Flush();
|
|
||||||
_stopHandle.Set();
|
|
||||||
State = ServerState.Stopped;
|
|
||||||
IsRunning = false;
|
|
||||||
Process.GetCurrentProcess().Kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Restart the program. DOES NOT SAVE!
|
|
||||||
/// </summary>
|
|
||||||
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();
|
|
||||||
Process.GetCurrentProcess().Kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override Task Save(long callerId)
|
public override Task Save(long callerId)
|
||||||
@@ -357,7 +333,8 @@ namespace Torch.Server
|
|||||||
}
|
}
|
||||||
if (MySession.Static.Players.TryGetPlayerId(callerId, out MyPlayer.PlayerId result))
|
if (MySession.Static.Players.TryGetPlayerId(callerId, out MyPlayer.PlayerId result))
|
||||||
{
|
{
|
||||||
Managers.GetManager<IChatManagerServer>()?.SendMessageAsOther("Server", response, statusCode == SaveGameStatus.Success ? MyFontEnum.Green : MyFontEnum.Red, result.SteamId);
|
Managers.GetManager<IChatManagerServer>()?.SendMessageAsOther("Server", response,
|
||||||
|
statusCode == SaveGameStatus.Success ? MyFontEnum.Green : MyFontEnum.Red, result.SteamId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -66,7 +66,7 @@ namespace Torch.Server
|
|||||||
private void BtnStart_Click(object sender, RoutedEventArgs e)
|
private void BtnStart_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_server.GetManager<InstanceManager>().SaveConfig();
|
_server.GetManager<InstanceManager>().SaveConfig();
|
||||||
new Thread(_server.Start).Start();
|
_server.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
|
|
||||||
@@ -41,9 +42,16 @@ namespace Torch.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ReflectionTestManager Init(Assembly asm)
|
public ReflectionTestManager Init(Assembly asm)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
foreach (Type type in asm.GetTypes())
|
foreach (Type type in asm.GetTypes())
|
||||||
Init(type);
|
Init(type);
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException e)
|
||||||
|
{
|
||||||
|
throw e.LoaderExceptions[0];
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
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.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
|
@@ -38,7 +38,9 @@ namespace Torch.Collections
|
|||||||
|
|
||||||
~MtObservableCollection()
|
~MtObservableCollection()
|
||||||
{
|
{
|
||||||
_flushEventQueue.Dispose();
|
Timer queue = _flushEventQueue;
|
||||||
|
_flushEventQueue = null;
|
||||||
|
queue?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -208,10 +210,10 @@ namespace Torch.Collections
|
|||||||
return;
|
return;
|
||||||
_collectionEventQueue.Enqueue(e);
|
_collectionEventQueue.Enqueue(e);
|
||||||
// In half a second, flush the events
|
// In half a second, flush the events
|
||||||
_flushEventQueue.Change(500, -1);
|
_flushEventQueue?.Change(500, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Timer _flushEventQueue;
|
private Timer _flushEventQueue;
|
||||||
|
|
||||||
private readonly Queue<NotifyCollectionChangedEventArgs> _collectionEventQueue =
|
private readonly Queue<NotifyCollectionChangedEventArgs> _collectionEventQueue =
|
||||||
new Queue<NotifyCollectionChangedEventArgs>();
|
new Queue<NotifyCollectionChangedEventArgs>();
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -36,7 +37,7 @@ namespace Torch.Event
|
|||||||
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton");
|
_log.Warn($"Registering type {type.FullName} as an event dispatch type, even though it isn't declared singleton");
|
||||||
var listsFound = 0;
|
var listsFound = 0;
|
||||||
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
|
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
|
||||||
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
|
||||||
if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(EventList<>))
|
if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(EventList<>))
|
||||||
{
|
{
|
||||||
Type eventType = field.FieldType.GenericTypeArguments[0];
|
Type eventType = field.FieldType.GenericTypeArguments[0];
|
||||||
@@ -70,7 +71,7 @@ namespace Torch.Event
|
|||||||
ParameterInfo[] ps = x.GetParameters();
|
ParameterInfo[] ps = x.GetParameters();
|
||||||
if (ps.Length != 1)
|
if (ps.Length != 1)
|
||||||
return false;
|
return false;
|
||||||
return ps[0].ParameterType.IsByRef && typeof(IEvent).IsAssignableFrom(ps[0].ParameterType);
|
return ps[0].ParameterType.IsByRef && typeof(IEvent).IsAssignableFrom(ps[0].ParameterType.GetElementType());
|
||||||
});
|
});
|
||||||
return exploreType.BaseType != null ? enumerable.Concat(EventHandlers(exploreType.BaseType)) : enumerable;
|
return exploreType.BaseType != null ? enumerable.Concat(EventHandlers(exploreType.BaseType)) : enumerable;
|
||||||
}
|
}
|
||||||
@@ -78,9 +79,12 @@ namespace Torch.Event
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
private static void RegisterHandlerInternal(IEventHandler instance)
|
private static void RegisterHandlerInternal(IEventHandler instance)
|
||||||
{
|
{
|
||||||
|
var foundHandler = false;
|
||||||
foreach (MethodInfo handler in EventHandlers(instance.GetType()))
|
foreach (MethodInfo handler in EventHandlers(instance.GetType()))
|
||||||
{
|
{
|
||||||
Type eventType = handler.GetParameters()[0].ParameterType;
|
Type eventType = handler.GetParameters()[0].ParameterType.GetElementType();
|
||||||
|
Debug.Assert(eventType != null);
|
||||||
|
foundHandler = true;
|
||||||
if (eventType.IsInterface)
|
if (eventType.IsInterface)
|
||||||
{
|
{
|
||||||
var foundList = false;
|
var foundList = false;
|
||||||
@@ -100,6 +104,9 @@ namespace Torch.Event
|
|||||||
}
|
}
|
||||||
_log.Error($"Unable to find event handler list for event type {eventType.FullName}");
|
_log.Error($"Unable to find event handler list for event type {eventType.FullName}");
|
||||||
}
|
}
|
||||||
|
if (!foundHandler)
|
||||||
|
_log.Warn($"Found no handlers in {instance.GetType().FullName} or base types");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -14,7 +14,7 @@ namespace Torch.Event
|
|||||||
/// Event shims should be singleton, and have one (or more) fields that are of type <see cref="EventList{T}"/>.
|
/// Event shims should be singleton, and have one (or more) fields that are of type <see cref="EventList{T}"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
internal class EventShimAttribute : Attribute
|
public class EventShimAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -186,10 +186,10 @@ namespace Torch.Managers.PatchManager.MSIL
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma warning disable 169
|
#pragma warning disable 649
|
||||||
[ReflectedMethod(Name = "StackChange")]
|
[ReflectedMethod(Name = "StackChange")]
|
||||||
private static Func<OpCode, int> _stackChange;
|
private static Func<OpCode, int> _stackChange;
|
||||||
#pragma warning restore 169
|
#pragma warning restore 649
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Estimates the stack delta for this instruction.
|
/// Estimates the stack delta for this instruction.
|
||||||
|
@@ -97,6 +97,7 @@ namespace Torch.Session
|
|||||||
CurrentSession.Managers.AddManager(manager);
|
CurrentSession.Managers.AddManager(manager);
|
||||||
}
|
}
|
||||||
(CurrentSession as TorchSession)?.Attach();
|
(CurrentSession as TorchSession)?.Attach();
|
||||||
|
_log.Info($"Loaded torch session for {MySession.Static.Name}");
|
||||||
SetState(TorchSessionState.Loaded);
|
SetState(TorchSessionState.Loaded);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -115,7 +116,9 @@ namespace Torch.Session
|
|||||||
_log.Warn("Session unloading event occurred when we don't have a session.");
|
_log.Warn("Session unloading event occurred when we don't have a session.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_log.Info($"Unloading torch session for {_currentSession.KeenSession.Name}");
|
||||||
SetState(TorchSessionState.Unloading);
|
SetState(TorchSessionState.Unloading);
|
||||||
|
_currentSession.Detach();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -133,9 +136,8 @@ namespace Torch.Session
|
|||||||
_log.Warn("Session unloading event occurred when we don't have a session.");
|
_log.Warn("Session unloading event occurred when we don't have a session.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_log.Info($"Unloading torch session for {_currentSession.KeenSession.Name}");
|
_log.Info($"Unloaded torch session for {_currentSession.KeenSession.Name}");
|
||||||
SetState(TorchSessionState.Unloaded);
|
SetState(TorchSessionState.Unloaded);
|
||||||
_currentSession.Detach();
|
|
||||||
_currentSession = null;
|
_currentSession = null;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@@ -255,6 +255,7 @@
|
|||||||
<Compile Include="Views\CollectionEditor.xaml.cs">
|
<Compile Include="Views\CollectionEditor.xaml.cs">
|
||||||
<DependentUpon>CollectionEditor.xaml</DependentUpon>
|
<DependentUpon>CollectionEditor.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="VRageGame.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -12,6 +15,9 @@ using NLog;
|
|||||||
using ProtoBuf.Meta;
|
using ProtoBuf.Meta;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Sandbox.Engine.Multiplayer;
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Engine.Platform.VideoMode;
|
||||||
|
using Sandbox.Engine.Utils;
|
||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
using Sandbox.Game.Multiplayer;
|
using Sandbox.Game.Multiplayer;
|
||||||
using Sandbox.Game.Screens.Helpers;
|
using Sandbox.Game.Screens.Helpers;
|
||||||
@@ -19,6 +25,7 @@ using Sandbox.Game.World;
|
|||||||
using Sandbox.Graphics.GUI;
|
using Sandbox.Graphics.GUI;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SpaceEngineers.Game;
|
using SpaceEngineers.Game;
|
||||||
|
using SpaceEngineers.Game.GUI;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.API.ModAPI;
|
using Torch.API.ModAPI;
|
||||||
@@ -31,16 +38,22 @@ using Torch.Managers.PatchManager;
|
|||||||
using Torch.Patches;
|
using Torch.Patches;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
using Torch.Session;
|
using Torch.Session;
|
||||||
|
using VRage;
|
||||||
using VRage.Collections;
|
using VRage.Collections;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.Common;
|
using VRage.Game.Common;
|
||||||
using VRage.Game.Components;
|
using VRage.Game.Components;
|
||||||
using VRage.Game.ObjectBuilder;
|
using VRage.Game.ObjectBuilder;
|
||||||
|
using VRage.Game.SessionComponents;
|
||||||
|
using VRage.GameServices;
|
||||||
|
using VRage.Library;
|
||||||
using VRage.ObjectBuilders;
|
using VRage.ObjectBuilders;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
using VRage.Scripting;
|
using VRage.Scripting;
|
||||||
|
using VRage.Steam;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
|
using VRageRender;
|
||||||
|
|
||||||
namespace Torch
|
namespace Torch
|
||||||
{
|
{
|
||||||
@@ -67,8 +80,10 @@ namespace Torch
|
|||||||
/// Use only if necessary, prefer dependency injection.
|
/// Use only if necessary, prefer dependency injection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ITorchBase Instance { get; private set; }
|
public static ITorchBase Instance { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ITorchConfig Config { get; protected set; }
|
public ITorchConfig Config { get; protected set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Version TorchVersion { get; }
|
public Version TorchVersion { get; }
|
||||||
|
|
||||||
@@ -79,8 +94,10 @@ namespace Torch
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Version GameVersion { get; private set; }
|
public Version GameVersion { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string[] RunArgs { get; set; }
|
public string[] RunArgs { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
[Obsolete("Use GetManager<T>() or the [Dependency] attribute.")]
|
||||||
public IPluginManager Plugins { get; protected set; }
|
public IPluginManager Plugins { get; protected set; }
|
||||||
@@ -90,10 +107,13 @@ namespace Torch
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Action SessionLoading;
|
public event Action SessionLoading;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Action SessionLoaded;
|
public event Action SessionLoaded;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Action SessionUnloading;
|
public event Action SessionUnloading;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event Action SessionUnloaded;
|
public event Action SessionUnloaded;
|
||||||
|
|
||||||
@@ -120,7 +140,9 @@ namespace Torch
|
|||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
TorchVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
TorchVersion = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
TorchVersionVerbose = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? TorchVersion.ToString();
|
TorchVersionVerbose = Assembly.GetEntryAssembly()
|
||||||
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||||
|
?.InformationalVersion ?? TorchVersion.ToString();
|
||||||
RunArgs = new string[0];
|
RunArgs = new string[0];
|
||||||
|
|
||||||
Managers = new DependencyManager();
|
Managers = new DependencyManager();
|
||||||
@@ -140,6 +162,39 @@ namespace Torch
|
|||||||
Managers.AddManager(new EventManager(this));
|
Managers.AddManager(new EventManager(this));
|
||||||
Managers.AddManager(Plugins);
|
Managers.AddManager(Plugins);
|
||||||
TorchAPI.Instance = this;
|
TorchAPI.Instance = this;
|
||||||
|
|
||||||
|
GameStateChanged += (game, state) =>
|
||||||
|
{
|
||||||
|
if (state == TorchGameState.Created)
|
||||||
|
{
|
||||||
|
// If the attached assemblies change (MySandboxGame.ctor => MySandboxGame.ParseArgs => MyPlugins.RegisterFromArgs)
|
||||||
|
// attach assemblies to object factories again.
|
||||||
|
ObjectFactoryInitPatch.ForceRegisterAssemblies();
|
||||||
|
// safe to commit here; all important static ctors have run
|
||||||
|
PatchManager.CommitInternal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sessionManager.SessionStateChanged += (session, state) =>
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case TorchSessionState.Loading:
|
||||||
|
SessionLoading?.Invoke();
|
||||||
|
break;
|
||||||
|
case TorchSessionState.Loaded:
|
||||||
|
SessionLoaded?.Invoke();
|
||||||
|
break;
|
||||||
|
case TorchSessionState.Unloading:
|
||||||
|
SessionUnloading?.Invoke();
|
||||||
|
break;
|
||||||
|
case TorchSessionState.Unloaded:
|
||||||
|
SessionUnloaded?.Invoke();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Prefer using Managers.GetManager for global managers")]
|
[Obsolete("Prefer using Managers.GetManager for global managers")]
|
||||||
@@ -252,34 +307,30 @@ namespace Torch
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Torch Init/Destroy
|
||||||
|
|
||||||
|
protected abstract uint SteamAppId { get; }
|
||||||
|
protected abstract string SteamAppName { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void Init()
|
public virtual void Init()
|
||||||
{
|
{
|
||||||
Debug.Assert(!_init, "Torch instance is already initialized.");
|
Debug.Assert(!_init, "Torch instance is already initialized.");
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
SpaceEngineersGame.SetupBasicGameInfo();
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
// If the attached assemblies change (MySandboxGame.ctor => MySandboxGame.ParseArgs => MyPlugins.RegisterFromArgs)
|
|
||||||
// attach assemblies to object factories again.
|
|
||||||
ObjectFactoryInitPatch.ForceRegisterAssemblies();
|
ObjectFactoryInitPatch.ForceRegisterAssemblies();
|
||||||
GameStateChanged += (game, state) =>
|
|
||||||
{
|
|
||||||
if (state == TorchGameState.Created)
|
|
||||||
{
|
|
||||||
ObjectFactoryInitPatch.ForceRegisterAssemblies();
|
|
||||||
// safe to commit here; all important static ctors have run
|
|
||||||
PatchManager.CommitInternal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null, "MyPerGameSettings.BasicGameInfo.GameVersion != null");
|
Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null,
|
||||||
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", "."));
|
"MyPerGameSettings.BasicGameInfo.GameVersion != null");
|
||||||
|
GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText
|
||||||
|
.ToString().Replace("_", "."));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.Title = $"{Config.InstanceName} - Torch {TorchVersion}, SE {GameVersion}";
|
Console.Title = $"{Config.InstanceName} - Torch {TorchVersion}, SE {GameVersion}";
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Running as service
|
// Running without a console
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -292,11 +343,9 @@ namespace Torch
|
|||||||
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
|
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
|
||||||
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
|
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
|
||||||
|
|
||||||
MySession.OnLoading += OnSessionLoading;
|
_game = new VRageGame(this, TweakGameSettings, SteamAppName, SteamAppId, Config.InstancePath, RunArgs);
|
||||||
MySession.AfterLoading += OnSessionLoaded;
|
if (!_game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromMinutes(5)))
|
||||||
MySession.OnUnloading += OnSessionUnloading;
|
Log.Warn("Failed to wait for game to be initialized");
|
||||||
MySession.OnUnloaded += OnSessionUnloaded;
|
|
||||||
RegisterVRagePlugin();
|
|
||||||
Managers.GetManager<PluginManager>().LoadPlugins();
|
Managers.GetManager<PluginManager>().LoadPlugins();
|
||||||
Managers.Attach();
|
Managers.Attach();
|
||||||
_init = true;
|
_init = true;
|
||||||
@@ -306,109 +355,57 @@ namespace Torch
|
|||||||
PatchManager.CommitInternal();
|
PatchManager.CommitInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSessionLoading()
|
/// <inheritdoc />
|
||||||
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
Log.Debug("Session loading");
|
Managers.Detach();
|
||||||
try
|
_game.SignalDestroy();
|
||||||
{
|
if (!_game.WaitFor(VRageGame.GameState.Destroyed, TimeSpan.FromSeconds(15)))
|
||||||
SessionLoading?.Invoke();
|
Log.Warn("Failed to wait for the game to be destroyed");
|
||||||
}
|
_game = null;
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSessionLoaded()
|
#endregion
|
||||||
{
|
|
||||||
Log.Debug("Session loaded");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SessionLoaded?.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSessionUnloading()
|
private VRageGame _game;
|
||||||
{
|
|
||||||
Log.Debug("Session unloading");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SessionUnloading?.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSessionUnloaded()
|
|
||||||
{
|
|
||||||
Log.Debug("Session unloaded");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SessionUnloaded?.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hook into the VRage plugin system for updates.
|
/// Called after the basic game information is filled, but before the game is created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void RegisterVRagePlugin()
|
protected virtual void TweakGameSettings()
|
||||||
{
|
{
|
||||||
var fieldName = "m_plugins";
|
|
||||||
var pluginList = typeof(MyPlugins).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<IPlugin>;
|
|
||||||
if (pluginList == null)
|
|
||||||
throw new TypeLoadException($"{fieldName} field not found in {nameof(MyPlugins)}");
|
|
||||||
|
|
||||||
pluginList.Add(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual Task Save(long callerId)
|
public virtual Task Save(long callerId)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return SaveGameAsync(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual void Start()
|
public virtual void Start()
|
||||||
{
|
{
|
||||||
|
_game.SignalStart();
|
||||||
|
if (!_game.WaitFor(VRageGame.GameState.Running, TimeSpan.FromSeconds(15)))
|
||||||
|
Log.Warn("Failed to wait for the game to be started");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void Stop()
|
public virtual void Stop()
|
||||||
{
|
{
|
||||||
|
LogManager.Flush();
|
||||||
|
_game.SignalStop();
|
||||||
|
if (!_game.WaitFor(VRageGame.GameState.Stopped, TimeSpan.FromSeconds(15)))
|
||||||
|
Log.Warn("Failed to wait for the game to be stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void Restart()
|
public abstract void Restart();
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
Managers.Detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void Init(object gameInstance)
|
public virtual void Init(object gameInstance)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -435,6 +432,7 @@ namespace Torch
|
|||||||
public event TorchGameStateChangedDel GameStateChanged;
|
public event TorchGameStateChangedDel GameStateChanged;
|
||||||
|
|
||||||
private static readonly HashSet<Assembly> _registeredCoreAssemblies = new HashSet<Assembly>();
|
private static readonly HashSet<Assembly> _registeredCoreAssemblies = new HashSet<Assembly>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a core (Torch) assembly with the system, including its
|
/// Registers a core (Torch) assembly with the system, including its
|
||||||
/// <see cref="EventManager"/> shims, <see cref="PatchManager"/> shims, and <see cref="ReflectedManager"/> components.
|
/// <see cref="EventManager"/> shims, <see cref="PatchManager"/> shims, and <see cref="ReflectedManager"/> components.
|
||||||
|
@@ -237,8 +237,12 @@ namespace Torch.Utils
|
|||||||
argExp[i] = Expression.Convert(paramExp[i + 1], invokeTypes[i]);
|
argExp[i] = Expression.Convert(paramExp[i + 1], invokeTypes[i]);
|
||||||
else
|
else
|
||||||
argExp[i] = paramExp[i + 1];
|
argExp[i] = paramExp[i + 1];
|
||||||
|
Debug.Assert(methodInstance.DeclaringType != null);
|
||||||
|
Expression instanceExp = paramExp[0].Type != methodInstance.DeclaringType
|
||||||
|
? Expression.Convert(paramExp[0], methodInstance.DeclaringType)
|
||||||
|
: (Expression) paramExp[0];
|
||||||
field.SetValue(null,
|
field.SetValue(null,
|
||||||
Expression.Lambda(Expression.Call(paramExp[0], methodInstance, argExp), paramExp)
|
Expression.Lambda(Expression.Call(instanceExp, methodInstance, argExp), paramExp)
|
||||||
.Compile());
|
.Compile());
|
||||||
_log.Trace($"Reflecting field {field.DeclaringType?.FullName}#{field.Name} with {methodInstance.DeclaringType?.FullName}#{methodInstance.Name}");
|
_log.Trace($"Reflecting field {field.DeclaringType?.FullName}#{field.Name} with {methodInstance.DeclaringType?.FullName}#{methodInstance.Name}");
|
||||||
}
|
}
|
||||||
@@ -318,7 +322,12 @@ namespace Torch.Utils
|
|||||||
Expression impl;
|
Expression impl;
|
||||||
if (attr is ReflectedSetterAttribute)
|
if (attr is ReflectedSetterAttribute)
|
||||||
{
|
{
|
||||||
impl = Expression.Block(Expression.Assign(fieldExp, paramExp[isStatic ? 0 : 1]), Expression.Default(typeof(void)));
|
var valParam = paramExp[isStatic ? 0 : 1];
|
||||||
|
var valExpr = (Expression)valParam;
|
||||||
|
var setType = sourceField?.FieldType ?? sourceProperty.PropertyType;
|
||||||
|
if (valParam.Type != setType)
|
||||||
|
valExpr = Expression.Convert(valExpr, setType);
|
||||||
|
impl = Expression.Block(Expression.Assign(fieldExp, valExpr), Expression.Default(typeof(void)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
310
Torch/VRageGame.cs
Normal file
310
Torch/VRageGame.cs
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
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.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Engine.Platform.VideoMode;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using SpaceEngineers.Game;
|
||||||
|
using SpaceEngineers.Game.GUI;
|
||||||
|
using Torch.Utils;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Audio;
|
||||||
|
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<List<IPlugin>> _getVRagePluginList;
|
||||||
|
|
||||||
|
[ReflectedGetter(Name = "Static", TypeName = "Sandbox.Game.Audio.MyMusicController, Sandbox.Game")]
|
||||||
|
private static readonly Func<object> _getMusicControllerStatic;
|
||||||
|
|
||||||
|
|
||||||
|
[ReflectedSetter(Name = "Static", TypeName = "Sandbox.Game.Audio.MyMusicController, Sandbox.Game")]
|
||||||
|
private static readonly Action<object> _setMusicControllerStatic;
|
||||||
|
|
||||||
|
|
||||||
|
[ReflectedMethod(Name = "Unload", TypeName = "Sandbox.Game.Audio.MyMusicController, Sandbox.Game")]
|
||||||
|
private static readonly Action<object> _musicControllerUnload;
|
||||||
|
#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<IMyGameService>(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 LoadSession(string sessionPath)
|
||||||
|
{
|
||||||
|
// ?
|
||||||
|
MySessionLoader.LoadSingleplayerSession(sessionPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadSession()
|
||||||
|
{
|
||||||
|
if (MySession.Static != null)
|
||||||
|
{
|
||||||
|
MySession.Static.Unload();
|
||||||
|
MySession.Static = null;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var musicCtl = _getMusicControllerStatic();
|
||||||
|
if (musicCtl != null)
|
||||||
|
{
|
||||||
|
_musicControllerUnload(musicCtl);
|
||||||
|
_setMusicControllerStatic(null);
|
||||||
|
MyAudio.Static.MusicAllowed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MyMultiplayer.Static != null)
|
||||||
|
{
|
||||||
|
MyMultiplayer.Static.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoStop()
|
||||||
|
{
|
||||||
|
ParallelTasks.Parallel.Scheduler.WaitForTasksToFinish(TimeSpan.FromSeconds(10.0));
|
||||||
|
MySandboxGame.Static.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals the game to stop itself.
|
||||||
|
/// </summary>
|
||||||
|
public void SignalStop()
|
||||||
|
{
|
||||||
|
_startGame = false;
|
||||||
|
_game.Invoke(DoStop, $"{nameof(VRageGame)}::{nameof(SignalStop)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals the game to start itself
|
||||||
|
/// </summary>
|
||||||
|
public void SignalStart()
|
||||||
|
{
|
||||||
|
_startGame = true;
|
||||||
|
_commandChanged.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals the game to destroy itself
|
||||||
|
/// </summary>
|
||||||
|
public void SignalDestroy()
|
||||||
|
{
|
||||||
|
_destroyGame = true;
|
||||||
|
SignalStop();
|
||||||
|
_commandChanged.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the game to transition to the given state
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">State to transition to</param>
|
||||||
|
/// <param name="timeout">Timeout</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,20 +1,17 @@
|
|||||||
|
$buildSalt = $Env:BUILD_NUMBER
|
||||||
$gitVersion = git describe --tags
|
$gitVersion = git describe --tags
|
||||||
$gitSimpleVersion = git describe --tags --abbrev=0
|
$gitSimpleVersion = git describe --tags --abbrev=0
|
||||||
if ($gitSimpleVersion.Equals($gitVersion)) {
|
$simpleVersionStandard = echo $gitSimpleVersion | Select-String -Pattern "([0-9]+)\.([0-9]+)\.([0-9]+)" | % {$_.Matches} | %{$_.Groups[1].Value+"."+$_.Groups[2].Value+"."+$_.Groups[3].Value}
|
||||||
$buildSalt = 0
|
$dotNetVersion = "$simpleVersionStandard.$buildSalt"
|
||||||
} else {
|
$infoVersion = "$gitVersion" -replace "([0-9]+)\.([0-9]+)\.([0-9]+)","$dotNetVersion"
|
||||||
$gitLatestCommit = git rev-parse --short HEAD
|
|
||||||
$buildSalt = [System.Numerics.BigInteger]::Abs([System.Numerics.BigInteger]::Parse($gitLatestCommit, [System.Globalization.NumberStyles]::HexNumber) % 16383) + 1
|
|
||||||
}
|
|
||||||
$dotNetVersion = echo $gitSimpleVersion | Select-String -Pattern "([0-9]+)\.([0-9]+)\.([0-9]+)" | % {$_.Matches} | %{$_.Groups[1].Value+"."+$_.Groups[2].Value+"."+$_.Groups[3].Value+".$buildSalt"}
|
|
||||||
|
|
||||||
$fileContent = @"
|
$fileContent = @"
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("$dotNetVersion")]
|
[assembly: AssemblyVersion("$dotNetVersion")]
|
||||||
[assembly: AssemblyInformationalVersion("$gitVersion")]
|
[assembly: AssemblyInformationalVersion("$infoVersion")]
|
||||||
"@
|
"@
|
||||||
|
|
||||||
echo $fileContent | Set-Content "$PSScriptRoot/AssemblyVersion.cs"
|
echo $fileContent | Set-Content "$PSScriptRoot/AssemblyVersion.cs"
|
||||||
|
|
||||||
echo "$gitVersion / $dotNetVersion"
|
echo "$infoVersion"
|
Reference in New Issue
Block a user