diff --git a/Torch.API/ITorchBase.cs b/Torch.API/ITorchBase.cs index ba766c5..edf21bb 100644 --- a/Torch.API/ITorchBase.cs +++ b/Torch.API/ITorchBase.cs @@ -101,10 +101,15 @@ namespace Torch.API Task Save(long callerId); /// - /// Initialize the Torch instance. + /// Initialize the Torch instance. Before this is invalid. /// void Init(); + /// + /// Disposes the Torch instance. After this is invalid. + /// + void Dispose(); + /// /// The current state of the game this instance of torch is controlling. /// diff --git a/Torch.Client/Torch.Client.csproj b/Torch.Client/Torch.Client.csproj index 9ed8a34..40f2506 100644 --- a/Torch.Client/Torch.Client.csproj +++ b/Torch.Client/Torch.Client.csproj @@ -127,7 +127,6 @@ - @@ -140,6 +139,9 @@ Settings.settings True + + + ResXFileCodeGenerator Resources.Designer.cs diff --git a/Torch.Client/TorchClient.cs b/Torch.Client/TorchClient.cs index 839c40a..22d2d15 100644 --- a/Torch.Client/TorchClient.cs +++ b/Torch.Client/TorchClient.cs @@ -14,6 +14,7 @@ using Torch.API; using Torch.API.Managers; using Torch.API.Session; using Torch.Client.Manager; +using Torch.Client.UI; using Torch.Session; using VRage; using VRage.FileSystem; @@ -27,7 +28,9 @@ namespace Torch.Client { private MyCommonProgramStartup _startup; private IMyRender _renderer; - private const uint APP_ID = 244850; + + protected override uint SteamAppId => 244850; + protected override string SteamAppName => "Space Engineers"; public TorchClient() { @@ -46,10 +49,9 @@ namespace Torch.Client Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias); MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries); Log.Info("Initializing Torch Client"); - base.Init(); - - SpaceEngineersGame.SetupBasicGameInfo(); _startup = new MyCommonProgramStartup(RunArgs); + SpaceEngineersGame.SetupBasicGameInfo(); + SpaceEngineersGame.SetupPerGameSettings(); if (_startup.PerformReporting()) 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."); var appDataPath = _startup.GetAppDataPath(); - MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath); - MyInitializer.InitCheckSum(); - _startup.InitSplashScreen(); - if (!_startup.Check64Bit()) - throw new InvalidOperationException("Torch requires a 64bit operating system"); + Config.InstancePath = appDataPath; + base.Init(); + OverrideMenus(); + SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}"); + } - _startup.DetectSharpDxLeaksBeforeRun(); - var steamService = new SteamService(Game.IsDedicated, APP_ID); - MyServiceManager.Instance.AddService(steamService); - _renderer = null; - 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()); + public override void Dispose() + { + base.Dispose(); + _startup.DetectSharpDxLeaksAfterRun(); } private void OverrideMenus() @@ -95,18 +86,6 @@ namespace Torch.Client 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) { @@ -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}"); - } - - 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); - } + throw new NotImplementedException(); } } } diff --git a/Torch.Client/TorchMainMenuScreen.cs b/Torch.Client/TorchMainMenuScreen.cs deleted file mode 100644 index 0bd9e5c..0000000 --- a/Torch.Client/TorchMainMenuScreen.cs +++ /dev/null @@ -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) - { - } - /// - 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()); - } - } -} diff --git a/Torch.Client/UI/TorchMainMenuScreen.cs b/Torch.Client/UI/TorchMainMenuScreen.cs new file mode 100644 index 0000000..2ccbfe6 --- /dev/null +++ b/Torch.Client/UI/TorchMainMenuScreen.cs @@ -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 _elementsGroup; +#pragma warning restore 169 + + public TorchMainMenuScreen() : this(false) + { + } + + public TorchMainMenuScreen(bool pauseGame) + : base(pauseGame) + { + } + /// + 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()); + } + } +} diff --git a/Torch.Client/UI/TorchNavScreen.cs b/Torch.Client/UI/TorchNavScreen.cs new file mode 100644 index 0000000..9b657a2 --- /dev/null +++ b/Torch.Client/UI/TorchNavScreen.cs @@ -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()); + }, 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(); + } +} diff --git a/Torch.Client/UI/TorchSettingsScreen.cs b/Torch.Client/UI/TorchSettingsScreen.cs new file mode 100644 index 0000000..07f4726 --- /dev/null +++ b/Torch.Client/UI/TorchSettingsScreen.cs @@ -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); + } + + /// + public override string GetFriendlyName() => "Torch Settings"; + + public void OnBackClick(MyGuiControlButton sender) => CloseScreen(); + } +} diff --git a/Torch.Server/Managers/InstanceManager.cs b/Torch.Server/Managers/InstanceManager.cs index f11a446..ab26246 100644 --- a/Torch.Server/Managers/InstanceManager.cs +++ b/Torch.Server/Managers/InstanceManager.cs @@ -32,23 +32,13 @@ namespace Torch.Server.Managers { } - - /// - 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) { if (validate) ValidateInstance(path); MyFileSystem.Reset(); - MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64"); MyFileSystem.Init("Content", path); //Initializes saves path. Why this isn't in Init() we may never know. MyFileSystem.InitUserSpecific(null); diff --git a/Torch.Server/Managers/MultiplayerManagerDedicatedEventShim.cs b/Torch.Server/Managers/MultiplayerManagerDedicatedEventShim.cs index d0d7433..c9a302d 100644 --- a/Torch.Server/Managers/MultiplayerManagerDedicatedEventShim.cs +++ b/Torch.Server/Managers/MultiplayerManagerDedicatedEventShim.cs @@ -24,20 +24,9 @@ namespace Torch.Server.Managers } } -// class AdminsCanJoinExample : IEventHandler -// { -// [EventHandler(SkipCancelled = false)] -// public void Handle(ref ValidateAuthTicketEvent info) -// { -// if (MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString()) -// || (info.ServerGroup == MySandboxGame.ConfigDedicated.GroupID && info.Officer)) -// info.Verdict = JoinResult.OK; -// } -// } - /// - /// Event that occurs when a player tries to connect to the server. - /// Use these values to choose a , + /// Event that occurs when a player tries to connect to a dedicated server. + /// Use these values to choose a , /// or leave it unset to allow the default logic to handle the request. /// public struct ValidateAuthTicketEvent : IEvent @@ -73,7 +62,7 @@ namespace Torch.Server.Managers public readonly bool Officer; /// - /// A future verdict on this authorization request. If null, let the default logic choose. + /// A future verdict on this authorization request. If null, let the default logic choose. If not async use /// public Task FutureVerdict; diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs index 81bf533..d97d6fe 100644 --- a/Torch.Server/TorchServer.cs +++ b/Torch.Server/TorchServer.cs @@ -44,19 +44,58 @@ namespace Torch.Server { //public MyConfigDedicated DedicatedConfig { get; set; } /// - public float SimulationRatio { get => _simRatio; set { _simRatio = value; OnPropertyChanged(); } } + public float SimulationRatio + { + get => _simRatio; + set + { + _simRatio = value; + OnPropertyChanged(); + } + } + /// - public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } } + public TimeSpan ElapsedPlayTime + { + get => _elapsedPlayTime; + set + { + _elapsedPlayTime = value; + OnPropertyChanged(); + } + } + /// public Thread GameThread { get; private set; } + /// - public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } } + public ServerState State + { + get => _state; + private set + { + _state = value; + OnPropertyChanged(); + } + } + /// - public bool IsRunning { get => _isRunning; set { _isRunning = value; OnPropertyChanged(); } } + public bool IsRunning + { + get => _isRunning; + set + { + _isRunning = value; + OnPropertyChanged(); + } + } + /// public InstanceManager DedicatedInstance { get; } + /// public string InstanceName => Config?.InstanceName; + /// public string InstancePath => Config?.InstancePath; @@ -80,30 +119,20 @@ namespace Torch.Server sessionManager.AddFactory((x) => new MultiplayerManagerDedicated(this)); } + /// + protected override uint SteamAppId => 244850; + + /// + protected override string SteamAppName => "SpaceEngineersDedicated"; + /// public override void Init() { Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'"); + Sandbox.Engine.Platform.Game.IsDedicated = true; + 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().SessionStateChanged += OnSessionStateChanged; GetManager().LoadInstance(Config.InstancePath); } @@ -117,81 +146,64 @@ 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; - - /// public override void Start() { if (State != ServerState.Stopped) return; + State = ServerState.Starting; + IsRunning = true; + Log.Info("Starting server."); + MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model; DedicatedInstance.SaveConfig(); _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(); - // 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; - } + } + + /// + public override void Stop() + { + if (State == ServerState.Stopped) + Log.Error("Server is already stopped"); + Log.Info("Stopping server."); + base.Stop(); + Log.Info("Server stopped."); + LogManager.Flush(); + _stopHandle.Set(); - MySandboxGame.Log.Close(); State = ServerState.Stopped; + IsRunning = false; + } + + /// + /// Restart the program. DOES NOT SAVE! + /// + 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(); } /// public override void Init(object gameInstance) { base.Init(gameInstance); - State = ServerState.Running; - SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch"); + var game = gameInstance as MySandboxGame; + if (game != null && MySession.Static != null) + { + State = ServerState.Running; +// SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch"); + } + else + { + State = ServerState.Stopped; + } } /// @@ -205,18 +217,22 @@ namespace Torch.Server if (_watchdog == null && Config.TickTimeout > 0) { 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) { var mre = new ManualResetEvent(false); - ((TorchServer)state).Invoke(() => mre.Set()); + ((TorchServer) state).Invoke(() => mre.Set()); if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout))) { #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)); #else Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread)); @@ -278,47 +294,7 @@ namespace Torch.Server return stack; } - /// - 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(); - } - - /// - /// Restart the program. DOES NOT SAVE! - /// - 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(); - } + #endregion /// public override Task Save(long callerId) @@ -357,8 +333,9 @@ namespace Torch.Server } if (MySession.Static.Players.TryGetPlayerId(callerId, out MyPlayer.PlayerId result)) { - Managers.GetManager()?.SendMessageAsOther("Server", response, statusCode == SaveGameStatus.Success ? MyFontEnum.Green : MyFontEnum.Red, result.SteamId); + Managers.GetManager()?.SendMessageAsOther("Server", response, + statusCode == SaveGameStatus.Success ? MyFontEnum.Green : MyFontEnum.Red, result.SteamId); } } } -} +} \ No newline at end of file diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs index 879343d..a85e265 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs @@ -186,10 +186,10 @@ namespace Torch.Managers.PatchManager.MSIL -#pragma warning disable 169 +#pragma warning disable 649 [ReflectedMethod(Name = "StackChange")] private static Func _stackChange; -#pragma warning restore 169 +#pragma warning restore 649 /// /// Estimates the stack delta for this instruction. diff --git a/Torch/Session/TorchSessionManager.cs b/Torch/Session/TorchSessionManager.cs index 426b320..7d8ea14 100644 --- a/Torch/Session/TorchSessionManager.cs +++ b/Torch/Session/TorchSessionManager.cs @@ -97,6 +97,7 @@ namespace Torch.Session CurrentSession.Managers.AddManager(manager); } (CurrentSession as TorchSession)?.Attach(); + _log.Info($"Loaded torch session for {MySession.Static.Name}"); SetState(TorchSessionState.Loaded); } catch (Exception e) @@ -115,7 +116,9 @@ namespace Torch.Session _log.Warn("Session unloading event occurred when we don't have a session."); return; } + _log.Info($"Unloading torch session for {_currentSession.KeenSession.Name}"); SetState(TorchSessionState.Unloading); + _currentSession.Detach(); } catch (Exception e) { @@ -133,9 +136,8 @@ namespace Torch.Session _log.Warn("Session unloading event occurred when we don't have a session."); return; } - _log.Info($"Unloading torch session for {_currentSession.KeenSession.Name}"); + _log.Info($"Unloaded torch session for {_currentSession.KeenSession.Name}"); SetState(TorchSessionState.Unloaded); - _currentSession.Detach(); _currentSession = null; } catch (Exception e) diff --git a/Torch/TorchBase.cs b/Torch/TorchBase.cs index 0d93ac7..a187169 100644 --- a/Torch/TorchBase.cs +++ b/Torch/TorchBase.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime; using System.Runtime.CompilerServices; +using System.Security.Principal; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -12,6 +15,9 @@ using NLog; using ProtoBuf.Meta; using Sandbox; using Sandbox.Engine.Multiplayer; +using Sandbox.Engine.Networking; +using Sandbox.Engine.Platform.VideoMode; +using Sandbox.Engine.Utils; using Sandbox.Game; using Sandbox.Game.Multiplayer; using Sandbox.Game.Screens.Helpers; @@ -19,6 +25,7 @@ using Sandbox.Game.World; using Sandbox.Graphics.GUI; using Sandbox.ModAPI; using SpaceEngineers.Game; +using SpaceEngineers.Game.GUI; using Torch.API; using Torch.API.Managers; using Torch.API.ModAPI; @@ -31,16 +38,22 @@ using Torch.Managers.PatchManager; using Torch.Patches; using Torch.Utils; using Torch.Session; +using VRage; using VRage.Collections; using VRage.FileSystem; using VRage.Game; using VRage.Game.Common; using VRage.Game.Components; using VRage.Game.ObjectBuilder; +using VRage.Game.SessionComponents; +using VRage.GameServices; +using VRage.Library; using VRage.ObjectBuilders; using VRage.Plugins; using VRage.Scripting; +using VRage.Steam; using VRage.Utils; +using VRageRender; namespace Torch { @@ -67,8 +80,10 @@ namespace Torch /// Use only if necessary, prefer dependency injection. /// public static ITorchBase Instance { get; private set; } + /// public ITorchConfig Config { get; protected set; } + /// public Version TorchVersion { get; } @@ -79,8 +94,10 @@ namespace Torch /// public Version GameVersion { get; private set; } + /// public string[] RunArgs { get; set; } + /// [Obsolete("Use GetManager() or the [Dependency] attribute.")] public IPluginManager Plugins { get; protected set; } @@ -90,10 +107,13 @@ namespace Torch /// public event Action SessionLoading; + /// public event Action SessionLoaded; + /// public event Action SessionUnloading; + /// public event Action SessionUnloaded; @@ -120,7 +140,9 @@ namespace Torch Instance = this; TorchVersion = Assembly.GetExecutingAssembly().GetName().Version; - TorchVersionVerbose = Assembly.GetEntryAssembly().GetCustomAttribute()?.InformationalVersion ?? TorchVersion.ToString(); + TorchVersionVerbose = Assembly.GetEntryAssembly() + .GetCustomAttribute() + ?.InformationalVersion ?? TorchVersion.ToString(); RunArgs = new string[0]; Managers = new DependencyManager(); @@ -140,6 +162,39 @@ namespace Torch Managers.AddManager(new EventManager(this)); Managers.AddManager(Plugins); 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")] @@ -252,34 +307,30 @@ namespace Torch #endregion + #region Torch Init/Destroy + + protected abstract uint SteamAppId { get; } + protected abstract string SteamAppName { get; } + /// public virtual void Init() { Debug.Assert(!_init, "Torch instance is already initialized."); SpaceEngineersGame.SetupBasicGameInfo(); SpaceEngineersGame.SetupPerGameSettings(); - // If the attached assemblies change (MySandboxGame.ctor => MySandboxGame.ParseArgs => MyPlugins.RegisterFromArgs) - // attach assemblies to object factories again. 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"); - GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText.ToString().Replace("_", ".")); + Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null, + "MyPerGameSettings.BasicGameInfo.GameVersion != null"); + GameVersion = new Version(new MyVersion(MyPerGameSettings.BasicGameInfo.GameVersion.Value).FormattedText + .ToString().Replace("_", ".")); try { Console.Title = $"{Config.InstanceName} - Torch {TorchVersion}, SE {GameVersion}"; } catch { - // Running as service + // Running without a console } #if DEBUG @@ -292,11 +343,7 @@ namespace Torch Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}"); Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}"); - MySession.OnLoading += OnSessionLoading; - MySession.AfterLoading += OnSessionLoaded; - MySession.OnUnloading += OnSessionUnloading; - MySession.OnUnloaded += OnSessionUnloaded; - RegisterVRagePlugin(); + InitVRageInstance(); Managers.GetManager().LoadPlugins(); Managers.Attach(); _init = true; @@ -306,109 +353,156 @@ namespace Torch PatchManager.CommitInternal(); } - private void OnSessionLoading() + /// + public virtual void Dispose() { - Log.Debug("Session loading"); - try - { - SessionLoading?.Invoke(); - } - catch (Exception e) - { - Log.Error(e); - throw; - } + Managers.Detach(); + DisposeVRageInstance(); } - private void OnSessionLoaded() - { - Log.Debug("Session loaded"); - try - { - SessionLoaded?.Invoke(); - } - catch (Exception e) - { - Log.Error(e); - throw; - } - } + #endregion - private void OnSessionUnloading() - { - Log.Debug("Session unloading"); - try - { - SessionUnloading?.Invoke(); - } - catch (Exception e) - { - Log.Error(e); - throw; - } - } + #region VRage Instance Init/Destroy - private void OnSessionUnloaded() +#pragma warning disable 649 + [ReflectedGetter(Name = "m_plugins", Type = typeof(MyPlugins))] + private static readonly Func> _getVRagePluginList; +#pragma warning restore 649 + + protected SpaceEngineersGame GameInstance { get; private set; } + + /// + /// Sets up the VRage instance. + /// Any flags (ie ) must be set before this is called. + /// + protected virtual void InitVRageInstance() { - Log.Debug("Session unloaded"); - try + bool dedicated = Sandbox.Engine.Platform.Game.IsDedicated; + Environment.SetEnvironmentVariable("SteamAppId", SteamAppId.ToString()); + MyServiceManager.Instance.AddService(new MySteamService(dedicated, SteamAppId)); + if (dedicated && !MyGameService.HasGameServer) { - SessionUnloaded?.Invoke(); + Log.Warn("Steam service is not running! Please reinstall dedicated server."); + return; } - catch (Exception e) + + SpaceEngineersGame.SetupBasicGameInfo(); + SpaceEngineersGame.SetupPerGameSettings(); + MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion; + MySessionComponentExtDebug.ForceDisable = true; + MyPerGameSettings.SendLogToKeen = false; + // SpaceEngineersGame.SetupAnalytics(); + + MyFileSystem.ExePath = Path.GetDirectoryName(typeof(SpaceEngineersGame).Assembly.Location); + + TweakGameSettings(); + + MyInitializer.InvokeBeforeRun(SteamAppId, SteamAppName, Config.InstancePath); + // MyInitializer.InitCheckSum(); + + + // Hook into the VRage plugin system for updates. + _getVRagePluginList().Add(this); + + if (!MySandboxGame.IsReloading) + MyFileSystem.InitUserSpecific(dedicated ? null : MyGameService.UserId.ToString()); + MySandboxGame.IsReloading = dedicated; + + // render init { - Log.Error(e); - throw; + IMyRender renderer = null; + if (dedicated) + { + renderer = new MyNullRender(); + } + else + { + MyPerformanceSettings preset = MyGuiScreenOptionsGraphics.GetPreset(MyRenderQualityEnum.NORMAL); + MyRenderProxy.Settings.User = MyVideoSettingsManager.GetGraphicsSettingsFromConfig(ref preset) + .PerformanceSettings.RenderSettings; + MyStringId graphicsRenderer = MySandboxGame.Config.GraphicsRenderer; + if (graphicsRenderer == MySandboxGame.DirectX11RendererKey) + { + renderer = new MyDX11Render(new MyRenderSettings?(MyRenderProxy.Settings)); + if (!renderer.IsSupported) + { + MySandboxGame.Log.WriteLine( + "DirectX 11 renderer not supported. No renderer to revert back to."); + renderer = null; + } + } + if (renderer == null) + { + throw new MyRenderException( + "The current version of the game requires a Dx11 card. \\n For more information please see : http://blog.marekrosa.org/2016/02/space-engineers-news-full-source-code_26.html", + MyRenderExceptionEnum.GpuNotSupported); + } + MySandboxGame.Config.GraphicsRenderer = graphicsRenderer; + } + MyRenderProxy.Initialize(renderer); + MyRenderProxy.GetRenderProfiler().SetAutocommit(false); + MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint"); } + + GameInstance = new SpaceEngineersGame(RunArgs); } /// - /// Hook into the VRage plugin system for updates. + /// Called after the basic game information is filled, but before the game is created. /// - 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; - if (pluginList == null) - throw new TypeLoadException($"{fieldName} field not found in {nameof(MyPlugins)}"); - - pluginList.Add(this); } + /// + /// Tears down the VRage instance + /// + protected virtual void DisposeVRageInstance() + { + GameInstance.Dispose(); + GameInstance = null; + + MyGameService.ShutDown(); + + _getVRagePluginList().Remove(this); + + MyInitializer.InvokeAfterRun(); + } + + #endregion + /// public virtual Task Save(long callerId) { - return Task.CompletedTask; + return SaveGameAsync(null); } /// public virtual void Start() { - + if (MySandboxGame.FatalErrorDuringInit) + { + Log.Warn($"Failed to start sandbox game: fatal error during init"); + return; + } + GameInstance.Run(); } /// public virtual void Stop() { - + if (IsOnGameThread()) + MySandboxGame.Static.Exit(); + else + InvokeBlocking(MySandboxGame.Static.Exit); } /// - public virtual void Restart() - { - - } - - /// - public virtual void Dispose() - { - Managers.Detach(); - } + public abstract void Restart(); /// public virtual void Init(object gameInstance) { - } /// @@ -435,6 +529,7 @@ namespace Torch public event TorchGameStateChangedDel GameStateChanged; private static readonly HashSet _registeredCoreAssemblies = new HashSet(); + /// /// Registers a core (Torch) assembly with the system, including its /// shims, shims, and components. @@ -467,4 +562,4 @@ namespace Torch } } } -} +} \ No newline at end of file