diff --git a/Torch.API/ITorchConfig.cs b/Torch.API/ITorchConfig.cs index 84d4121..ae7e99d 100644 --- a/Torch.API/ITorchConfig.cs +++ b/Torch.API/ITorchConfig.cs @@ -23,6 +23,7 @@ namespace Torch string ChatName { get; set; } string ChatColor { get; set; } string TestPlugin { get; set; } + bool DisconnectOnRestart { get; set; } bool Save(string path = null); } diff --git a/Torch.Mod/Messages/JoinServerMessage.cs b/Torch.Mod/Messages/JoinServerMessage.cs new file mode 100644 index 0000000..ffc8ab0 --- /dev/null +++ b/Torch.Mod/Messages/JoinServerMessage.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ProtoBuf; +using Sandbox.ModAPI; + +namespace Torch.Mod.Messages +{ + [ProtoContract] + public class JoinServerMessage : MessageBase + { + [ProtoMember(201)] + public int Delay; + [ProtoMember(202)] + public string Address; + + private JoinServerMessage() + { + + } + + public JoinServerMessage(string address) + { + Address = address; + } + + public JoinServerMessage(string address, int delay) + { + Address = address; + Delay = delay; + } + + public override void ProcessClient() + { + if (TorchModCore.Debug) + { + MyAPIGateway.Utilities.ShowMessage("Torch", $"Joining server {Address} with delay {Delay}"); + } + + if (Delay <= 0) + { + MyAPIGateway.Multiplayer.JoinServer(Address); + return; + } + + MyAPIGateway.Parallel.StartBackground(() => + { + MyAPIGateway.Parallel.Sleep(Delay); + MyAPIGateway.Multiplayer.JoinServer(Address); + }); + } + + public override void ProcessServer() + { + } + } +} diff --git a/Torch.Mod/Messages/MessageBase.cs b/Torch.Mod/Messages/MessageBase.cs index 3a00738..55bc140 100644 --- a/Torch.Mod/Messages/MessageBase.cs +++ b/Torch.Mod/Messages/MessageBase.cs @@ -11,6 +11,7 @@ namespace Torch.Mod.Messages [ProtoInclude(1, typeof(DialogMessage))] [ProtoInclude(2, typeof(NotificationMessage))] [ProtoInclude(3, typeof(VoxelResetMessage))] + [ProtoInclude(4, typeof(JoinServerMessage))] #endregion [ProtoContract] @@ -28,7 +29,7 @@ namespace Torch.Mod.Messages internal ulong[] Ignore; internal byte[] CompressedData; } - + public enum MessageTarget { /// diff --git a/Torch.Mod/ModCommunication.cs b/Torch.Mod/ModCommunication.cs index 2c7b492..76be249 100644 --- a/Torch.Mod/ModCommunication.cs +++ b/Torch.Mod/ModCommunication.cs @@ -10,6 +10,7 @@ using Torch.Mod.Messages; using VRage; using VRage.Collections; using VRage.Game.ModAPI; +using VRage.Network; using VRage.Utils; using Task = ParallelTasks.Task; @@ -50,6 +51,10 @@ namespace Torch.Mod { var m = _messagePool.Get(); m.CompressedData = bytes; +#if TORCH + m.SenderId = MyEventContext.Current.Sender.Value; +#endif + _processing.Add(m); } @@ -59,10 +64,19 @@ namespace Torch.Mod { try { - var m = _processing.Take(); + MessageBase m; + try + { + m = _processing.Take(); + } + catch + { + continue; + } + MyLog.Default.WriteLineAndConsole($"Processing message: {m.GetType().Name}"); - if (m is IncomingMessage) + if (m is IncomingMessage) //process incoming messages { MessageBase i; try @@ -78,50 +92,55 @@ namespace Torch.Mod continue; } + if (TorchModCore.Debug) + MyAPIGateway.Utilities.ShowMessage("Torch", $"Received message of type {i.GetType().Name}"); + if (MyAPIGateway.Multiplayer.IsServer) i.ProcessServer(); else i.ProcessClient(); } - else + else //process outgoing messages { + if (TorchModCore.Debug) + MyAPIGateway.Utilities.ShowMessage("Torch", $"Sending message of type {m.GetType().Name}"); + var b = MyAPIGateway.Utilities.SerializeToBinary(m); m.CompressedData = MyCompression.Compress(b); - MyAPIGateway.Utilities.InvokeOnGameThread(() => + switch (m.TargetType) { + case MessageTarget.Single: + MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, m.Target); + break; + case MessageTarget.Server: + MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, m.CompressedData); + break; + case MessageTarget.AllClients: + MyAPIGateway.Players.GetPlayers(_playerCache); + foreach (var p in _playerCache) + { + if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId) + continue; + MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId); + } - switch (m.TargetType) - { - case MessageTarget.Single: - MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, m.Target); - break; - case MessageTarget.Server: - MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, m.CompressedData); - break; - case MessageTarget.AllClients: - MyAPIGateway.Players.GetPlayers(_playerCache); - foreach (var p in _playerCache) - { - if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId) - continue; - MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId); - } - break; - case MessageTarget.AllExcept: - MyAPIGateway.Players.GetPlayers(_playerCache); - foreach (var p in _playerCache) - { - if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || m.Ignore.Contains(p.SteamUserId)) - continue; - MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId); - } - break; - default: - throw new Exception(); - } - _playerCache.Clear(); - }); + break; + case MessageTarget.AllExcept: + MyAPIGateway.Players.GetPlayers(_playerCache); + foreach (var p in _playerCache) + { + if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || m.Ignore.Contains(p.SteamUserId)) + continue; + MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId); + } + + break; + default: + throw new Exception(); + } + + _playerCache.Clear(); } } catch (Exception ex) @@ -130,7 +149,7 @@ namespace Torch.Mod } } - MyLog.Default.WriteLineAndConsole("TORCH MOD: COMMUNICATION THREAD: EXIT SIGNAL RECEIVED!"); + MyLog.Default.WriteLineAndConsole("TORCH MOD: INFO: Communication thread shut down successfully! THIS IS NOT AN ERROR"); //exit signal received. Clean everything and GTFO _processing?.Dispose(); _processing = null; diff --git a/Torch.Mod/Torch.Mod.projitems b/Torch.Mod/Torch.Mod.projitems index 632d99a..8ab2e95 100644 --- a/Torch.Mod/Torch.Mod.projitems +++ b/Torch.Mod/Torch.Mod.projitems @@ -10,6 +10,7 @@ + diff --git a/Torch.Mod/TorchModCore.cs b/Torch.Mod/TorchModCore.cs index 83f3a33..877ca8c 100644 --- a/Torch.Mod/TorchModCore.cs +++ b/Torch.Mod/TorchModCore.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Sandbox.ModAPI; using VRage.Game.Components; namespace Torch.Mod @@ -12,6 +13,7 @@ namespace Torch.Mod { public const ulong MOD_ID = 1406994352; private static bool _init; + public static bool Debug; public override void UpdateAfterSimulation() { @@ -20,12 +22,24 @@ namespace Torch.Mod _init = true; ModCommunication.Register(); + MyAPIGateway.Utilities.MessageEntered += Utilities_MessageEntered; + } + + private void Utilities_MessageEntered(string messageText, ref bool sendToOthers) + { + if (messageText == "@!debug") + { + Debug = !Debug; + MyAPIGateway.Utilities.ShowMessage("Torch", $"Debug: {Debug}"); + sendToOthers = false; + } } protected override void UnloadData() { try { + MyAPIGateway.Utilities.MessageEntered -= Utilities_MessageEntered; ModCommunication.Unregister(); } catch diff --git a/Torch.Server/Managers/RemoteAPIManager.cs b/Torch.Server/Managers/RemoteAPIManager.cs index 4d7fe1c..645a426 100644 --- a/Torch.Server/Managers/RemoteAPIManager.cs +++ b/Torch.Server/Managers/RemoteAPIManager.cs @@ -17,12 +17,24 @@ namespace Torch.Server.Managers /// public override void Attach() { - if (MySandboxGame.ConfigDedicated.RemoteApiEnabled && !string.IsNullOrEmpty(MySandboxGame.ConfigDedicated.RemoteSecurityKey)) + Torch.GameStateChanged += TorchOnGameStateChanged; + base.Attach(); + } + + /// + public override void Detach() + { + Torch.GameStateChanged -= TorchOnGameStateChanged; + base.Detach(); + } + + private void TorchOnGameStateChanged(MySandboxGame game, TorchGameState newstate) + { + if (newstate == TorchGameState.Loading && MySandboxGame.ConfigDedicated.RemoteApiEnabled && !string.IsNullOrEmpty(MySandboxGame.ConfigDedicated.RemoteSecurityKey)) { var myRemoteServer = new MyRemoteServer(MySandboxGame.ConfigDedicated.RemoteApiPort, MySandboxGame.ConfigDedicated.RemoteSecurityKey); LogManager.GetCurrentClassLogger().Info($"Remote API started on port {myRemoteServer.Port}"); } - base.Attach(); } } } \ No newline at end of file diff --git a/Torch.Server/Program.cs b/Torch.Server/Program.cs index 9abb67c..3e10b1e 100644 --- a/Torch.Server/Program.cs +++ b/Torch.Server/Program.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.ServiceProcess; using System.Text; using System.Threading; +using Microsoft.VisualBasic.Devices; using NLog; using NLog.Fluent; using NLog.Targets; @@ -59,7 +60,8 @@ namespace Torch.Server return; } - if (!Environment.UserInteractive) + // Breaks on Windows Server 2019 + if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive) { using (var service = new TorchService()) ServiceBase.Run(service); diff --git a/Torch.Server/Torch.Server.csproj b/Torch.Server/Torch.Server.csproj index 2265911..1ebb566 100644 --- a/Torch.Server/Torch.Server.csproj +++ b/Torch.Server/Torch.Server.csproj @@ -83,6 +83,7 @@ ..\GameBinaries\Microsoft.CodeAnalysis.CSharp.dll False + ..\packages\Microsoft.Win32.Registry.4.4.0\lib\net461\Microsoft.Win32.Registry.dll diff --git a/Torch.Server/TorchConfig.cs b/Torch.Server/TorchConfig.cs index 1172b9b..d4c1e86 100644 --- a/Torch.Server/TorchConfig.cs +++ b/Torch.Server/TorchConfig.cs @@ -94,6 +94,9 @@ namespace Torch.Server [Arg("localplugins", "Loads all pluhins from disk, ignores the plugins defined in config.")] public bool LocalPlugins { get; set; } + [Arg("disconnect", "When server restarts, all clients are rejected to main menu to prevent auto rejoin")] + public bool DisconnectOnRestart { get; set; } + public string ChatName { get; set; } = "Server"; public string ChatColor { get; set; } = "Red"; diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs index 7835f39..d017354 100644 --- a/Torch.Server/TorchServer.cs +++ b/Torch.Server/TorchServer.cs @@ -19,6 +19,7 @@ using Torch.API.Managers; using Torch.API.Session; using Torch.Commands; using Torch.Mod; +using Torch.Mod.Messages; using Torch.Server.Commands; using Torch.Server.Managers; using Torch.Utils; @@ -152,6 +153,11 @@ namespace Torch.Server /// public override void Restart() { + if (Config.DisconnectOnRestart) + { + ModCommunication.SendMessageToClients(new JoinServerMessage("0.0.0.0:25555")); + } + if (IsRunning) Save().ContinueWith(DoRestart, this, TaskContinuationOptions.RunContinuationsAsynchronously); else diff --git a/Torch.Server/Views/ConfigControl.xaml.cs b/Torch.Server/Views/ConfigControl.xaml.cs index ee9de3c..486c4e6 100644 --- a/Torch.Server/Views/ConfigControl.xaml.cs +++ b/Torch.Server/Views/ConfigControl.xaml.cs @@ -13,6 +13,7 @@ using Torch.Server.Annotations; using Torch.Server.Managers; using Torch.Server.ViewModels; using Torch.Views; +using VRage.Game.ModAPI; namespace Torch.Server.Views { @@ -129,7 +130,9 @@ namespace Torch.Server.Views //var w = new RoleEditor(_instanceManager.DedicatedConfig.SelectedWorld); //w.Show(); var d = new RoleEditor(); - d.Edit(_instanceManager.DedicatedConfig.SelectedWorld.Checkpoint.PromotedUsers.Dictionary); + var w = _instanceManager.DedicatedConfig.SelectedWorld; + d.Edit(w.Checkpoint.PromotedUsers.Dictionary); + _instanceManager.DedicatedConfig.Administrators = w.Checkpoint.PromotedUsers.Dictionary.Where(k => k.Value >= MyPromoteLevel.Admin).Select(k => k.Key.ToString()).ToList(); } } } diff --git a/Torch/Plugins/PluginManager.cs b/Torch/Plugins/PluginManager.cs index 28b017e..86c68c2 100644 --- a/Torch/Plugins/PluginManager.cs +++ b/Torch/Plugins/PluginManager.cs @@ -124,7 +124,7 @@ namespace Torch.Managers if (!string.IsNullOrEmpty(Torch.Config.TestPlugin)) { _log.Info($"Loading plugin for debug at {Torch.Config.TestPlugin}"); - + foreach (var item in GetLocalPlugins(Torch.Config.TestPlugin, true)) { _log.Info(item.Path);