From 36832451936d5abb20330f21363387f584686b3b Mon Sep 17 00:00:00 2001 From: misterbubb Date: Thu, 18 Dec 2025 18:35:18 -0600 Subject: [PATCH] Add restart command with IPC to launcher --- Nitrox.Launcher/Models/Design/ServerEntry.cs | 16 ++++++++++ Nitrox.Model/Helper/Ipc.cs | 3 +- .../Models/Commands/RestartCommand.cs | 30 +++++++++++++++++++ Nitrox.Server.Subnautica/Program.cs | 4 +++ Nitrox.Server.Subnautica/Server.cs | 11 +++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 Nitrox.Server.Subnautica/Models/Commands/RestartCommand.cs diff --git a/Nitrox.Launcher/Models/Design/ServerEntry.cs b/Nitrox.Launcher/Models/Design/ServerEntry.cs index d7a5bc5c81..9cfa22ea1a 100644 --- a/Nitrox.Launcher/Models/Design/ServerEntry.cs +++ b/Nitrox.Launcher/Models/Design/ServerEntry.cs @@ -223,12 +223,21 @@ public void Start(string savesDir, int existingProcessId = 0, Action? onExited = if (Process != null) { Process.PlayerCountChanged += count => Dispatcher.UIThread.Post(() => Players = count); + Process.RestartRequested += () => Dispatcher.UIThread.Post(() => _ = RestartAsync(savesDir, onExited)); } IsNewServer = false; IsOnline = true; } + private async Task RestartAsync(string savesDir, Action? onExited) + { + Log.Info($"Restarting server '{Name}'..."); + await StopAsync(); + Start(savesDir, onExited: onExited); + Log.Info($"Server '{Name}' restarted successfully"); + } + [RelayCommand(AllowConcurrentExecutions = false)] public async Task StopAsync() { @@ -346,6 +355,12 @@ private ServerProcess(string saveDir, Action onExited, bool isEmbeddedMode = fal return; } + if (output == Ipc.Messages.RestartMessage) + { + RestartRequested?.Invoke(); + return; + } + // Ignore any messages that are part of the IPC message protocol if (Ipc.Messages.AllMessages.Any(msg => output.StartsWith($"{msg}:", StringComparison.Ordinal))) { @@ -396,6 +411,7 @@ private ServerProcess(string saveDir, Action onExited, bool isEmbeddedMode = fal public static ServerProcess Start(string saveDir, Action onExited, bool isEmbedded, int processId) => new(saveDir, onExited, isEmbedded, processId); public event Action? PlayerCountChanged; + public event Action? RestartRequested; /// /// Tries to close the server gracefully with a timeout of 7 seconds. If it fails, returns false. diff --git a/Nitrox.Model/Helper/Ipc.cs b/Nitrox.Model/Helper/Ipc.cs index c34229f19e..60641afca6 100644 --- a/Nitrox.Model/Helper/Ipc.cs +++ b/Nitrox.Model/Helper/Ipc.cs @@ -20,8 +20,9 @@ public static class Messages public static string StopMessage => "__SERVER_STOPPED__"; public static string SaveNameMessage => "__SAVE_NAME__"; public static string PlayerCountMessage => "__PLAYER_COUNT__"; + public static string RestartMessage => "__SERVER_RESTART__"; - public static List AllMessages { get; } = [StopMessage, SaveNameMessage, PlayerCountMessage]; + public static List AllMessages { get; } = [StopMessage, SaveNameMessage, PlayerCountMessage, RestartMessage]; } public sealed class ServerIpc : IDisposable diff --git a/Nitrox.Server.Subnautica/Models/Commands/RestartCommand.cs b/Nitrox.Server.Subnautica/Models/Commands/RestartCommand.cs new file mode 100644 index 0000000000..91b96e505f --- /dev/null +++ b/Nitrox.Server.Subnautica/Models/Commands/RestartCommand.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Nitrox.Model.DataStructures.GameLogic; +using Nitrox.Model.Helper; +using Nitrox.Server.Subnautica.Models.Commands.Abstract; + +namespace Nitrox.Server.Subnautica.Models.Commands; + +internal class RestartCommand : Command +{ + public override IEnumerable Aliases { get; } = ["reboot"]; + + public RestartCommand() : base("restart", Perms.ADMIN, "Saves and restarts the server") + { + } + + protected override void Execute(CallArgs args) + { + Server server = Server.Instance; + if (!server.IsRunning) + { + SendMessage(args.Sender, "Server is not running"); + return; + } + + SendMessageToAllPlayers("Server is restarting..."); + Log.Info("Server restart requested via command"); + + server.RequestRestart(); + } +} diff --git a/Nitrox.Server.Subnautica/Program.cs b/Nitrox.Server.Subnautica/Program.cs index fa2ba4a29c..75f9508c16 100644 --- a/Nitrox.Server.Subnautica/Program.cs +++ b/Nitrox.Server.Subnautica/Program.cs @@ -93,6 +93,10 @@ private static async Task StartServer(string[] args) { _ = ipc.SendOutput($"{Ipc.Messages.PlayerCountMessage}:[{count}]"); }; + server.RestartRequested += () => + { + _ = ipc.SendOutput(Ipc.Messages.RestartMessage); + }; string serverSaveName = Server.GetSaveName(args); Log.SaveName = serverSaveName; diff --git a/Nitrox.Server.Subnautica/Server.cs b/Nitrox.Server.Subnautica/Server.cs index 7fe7c0749f..10ff44a368 100644 --- a/Nitrox.Server.Subnautica/Server.cs +++ b/Nitrox.Server.Subnautica/Server.cs @@ -40,6 +40,7 @@ public class Server public int Port => serverConfig?.ServerPort ?? -1; public event Action? PlayerCountChanged; + public event Action? RestartRequested; public Server(WorldPersistence worldPersistence, World world, SubnauticaServerConfig serverConfig, Models.Communication.NitroxServer server, WorldEntityManager worldEntityManager, EntityRegistry entityRegistry) { @@ -339,6 +340,16 @@ public void ResumeServer() Log.Info("Server has resumed"); } + public void RequestRestart() + { + if (!IsRunning) + { + return; + } + Log.Info("Server restart requested"); + RestartRequested?.Invoke(); + } + private static List GetSaves() { try