diff --git a/src/ARKServerManager.Common/ARKServerManager.Common.csproj b/src/ARKServerManager.Common/ARKServerManager.Common.csproj index 7f2c4d66..12828701 100644 --- a/src/ARKServerManager.Common/ARKServerManager.Common.csproj +++ b/src/ARKServerManager.Common/ARKServerManager.Common.csproj @@ -1,6 +1,6 @@  - Debug;Release;Debug - Beta + Debug;Release net462 diff --git a/src/ARKServerManager/ARKServerManager.csproj b/src/ARKServerManager/ARKServerManager.csproj index 15616b94..b505318f 100644 --- a/src/ARKServerManager/ARKServerManager.csproj +++ b/src/ARKServerManager/ARKServerManager.csproj @@ -136,6 +136,16 @@ true + + true + bin\Debug - ServerMonitor\ + TRACE;DEBUG + full + AnyCPU + 7.3 + prompt + true + ..\packages\DotNetZip.1.13.8\lib\net40\DotNetZip.dll @@ -356,6 +366,7 @@ + Always diff --git a/src/ARKServerManager/Art/Restart.ico b/src/ARKServerManager/Art/Restart.ico new file mode 100644 index 00000000..3e07dd1e Binary files /dev/null and b/src/ARKServerManager/Art/Restart.ico differ diff --git a/src/ARKServerManager/Globalization/en-US/en-US.xaml b/src/ARKServerManager/Globalization/en-US/en-US.xaml index dc7d809c..597c4289 100644 --- a/src/ARKServerManager/Globalization/en-US/en-US.xaml +++ b/src/ARKServerManager/Globalization/en-US/en-US.xaml @@ -440,6 +440,7 @@ Server Monitor + Selected Total Servers: Server Map @@ -449,6 +450,16 @@ Status Create a desktop shortcut to open this form directly. + Start the selected servers. + Shutdown Selected Servers + Shutdown the selected servers. + Stop Selected Servers + Stop the selected servers. + Restart the selected servers. + Update the selected servers. + Backup the selected servers. + Select all servers. + Unselect all servers. Open the Player List window. Open the RCON window. Start the server. @@ -459,7 +470,23 @@ Server Update Error Another server is being upgraded, wait until the upgrade has finished and try again. Confirm Window Close - You are currently perform a server update, closing the window with disconnect you from steamcmd. Do you want to continue closing the window? + You are currently performing a server update, closing the window will disconnect you from steamcmd. Do you want to continue closing the window? + Confirm Shutdown Servers + You are about to shutdown the selected servers. Do you want to continue? + Confirm Start Servers + You are about to start the selected servers. Do you want to continue? + Confirm Stop Servers + You are about to stop the selected servers. Do you want to continue? + Confirm Restart Servers + You are about to restart the selected servers. Do you want to continue? + Confirm Update Servers + You are about to update the selected servers. Do you want to continue? + Confirm Backup Servers + You are about to backup the selected servers. Do you want to continue? + Selected Servers Error + You have not selected any servers. Selected one or more servers in the list and try again. + Close Server Monitor Error + The server monitor window cannot be closed at this time. One or more of the servers is currently starting, shutting down or restarting. diff --git a/src/ARKServerManager/Lib/ServerApp.cs b/src/ARKServerManager/Lib/ServerApp.cs index 091d84ee..db318e9e 100644 --- a/src/ARKServerManager/Lib/ServerApp.cs +++ b/src/ARKServerManager/Lib/ServerApp.cs @@ -101,6 +101,7 @@ namespace ServerManagerTool.Lib public bool DeleteOldBackupFiles = Config.Default.AutoBackup_DeleteOldFiles; public int ExitCode = EXITCODE_NORMALEXIT; public bool OutputLogs = false; + public bool PerformWorldSave = Config.Default.ServerShutdown_EnableWorldSave; public bool SendAlerts = false; public bool SendEmails = false; public string ShutdownReason = null; @@ -257,11 +258,14 @@ namespace ServerManagerTool.Lib ServerStatusChangeCallback?.Invoke(ServerStatus.Stopped); - // make a backup of the current profile and config files. - CreateProfileBackupArchiveFile(_profile); + if (ServerProcess != ServerProcessType.Stop) + { + // make a backup of the current profile and config files. + CreateProfileBackupArchiveFile(_profile); - if (ExitCode != EXITCODE_NORMALEXIT) - return; + if (ExitCode != EXITCODE_NORMALEXIT) + return; + } if (BackupWorldFile) { @@ -284,10 +288,10 @@ namespace ServerManagerTool.Lib { ServerStatusChangeCallback?.Invoke(ServerStatus.Stopped); } - } - if (ExitCode != EXITCODE_NORMALEXIT) - return; + if (ExitCode != EXITCODE_NORMALEXIT) + return; + } // check if this is a shutdown only, or a shutdown and restart. if (restartServer) @@ -579,7 +583,7 @@ namespace ServerManagerTool.Lib } // check if we need to perform a world save (not required for SotF servers) - if (serverAccessible && Config.Default.ServerShutdown_EnableWorldSave && !_profile.SotFEnabled) + if (serverAccessible && PerformWorldSave && !_profile.SotFEnabled) { try { @@ -670,35 +674,38 @@ namespace ServerManagerTool.Lib process.Exited += handler; // Method 1 - Shutdown Command - if (serverAccessible && _profile.RCONEnabled && Config.Default.ServerShutdown_UseShutdownCommand) + if (ServerProcess != ServerProcessType.Stop) { - try + if (serverAccessible && _profile.RCONEnabled && Config.Default.ServerShutdown_UseShutdownCommand) { - sent = SendCommand(Config.Default.ServerShutdownCommand, cancellationToken); - if (sent) + try { - Task.Delay(10000, cancellationToken).Wait(cancellationToken); + sent = SendCommand(Config.Default.ServerShutdownCommand, cancellationToken); + if (sent) + { + Task.Delay(10000, cancellationToken).Wait(cancellationToken); + } + } + catch (Exception ex) + { + Debug.WriteLine($"RCON> {Config.Default.ServerShutdownCommand} command.\r\n{ex.Message}"); } - } - catch (Exception ex) - { - Debug.WriteLine($"RCON> {Config.Default.ServerShutdownCommand} command.\r\n{ex.Message}"); - } - if (sent && !process.HasExited) - { - ts.Task.Wait(60000); // 1 minute - } + if (sent && !process.HasExited) + { + ts.Task.Wait(60000); // 1 minute + } - if (process.HasExited) - { - LogProfileMessage($"Exited server successfully."); - LogProfileMessage(""); - ExitCode = EXITCODE_NORMALEXIT; - return; - } + if (process.HasExited) + { + LogProfileMessage($"Exited server successfully."); + LogProfileMessage(""); + ExitCode = EXITCODE_NORMALEXIT; + return; + } - LogProfileMessage("Exiting server timed out, attempting to close the server."); + LogProfileMessage("Exiting server timed out, attempting to close the server."); + } } // Method 2 - Close the process diff --git a/src/ARKServerManager/Utils/DiscordBotHelper.cs b/src/ARKServerManager/Utils/DiscordBotHelper.cs index 1de7d20c..152c5de6 100644 --- a/src/ARKServerManager/Utils/DiscordBotHelper.cs +++ b/src/ARKServerManager/Utils/DiscordBotHelper.cs @@ -58,11 +58,11 @@ namespace ServerManagerTool.Utils return new List { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) }; case CommandType.Shutdown: if (Config.Default.AllowDiscordShutdown) - return ShutdownServer(channelId, profileIdOrAlias, token); + return StopServer(channelId, profileIdOrAlias, true, token); return new List { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) }; case CommandType.Stop: if (Config.Default.AllowDiscordStop) - return StopServer(channelId, profileIdOrAlias, token); + return StopServer(channelId, profileIdOrAlias, false, token); return new List { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) }; case CommandType.Start: if (Config.Default.AllowDiscordStart) @@ -475,112 +475,7 @@ namespace ServerManagerTool.Utils return responseList; } - private static IList ShutdownServer(string channelId, string profileIdOrAlias, CancellationToken token) - { - if (string.IsNullOrWhiteSpace(profileIdOrAlias)) - { - return new List { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), CommandType.Shutdown) }; - } - - var profileList = new List(); - var responseList = new List(); - - TaskUtils.RunOnUIThreadAsync(() => - { - var serverList = ServerManager.Instance.Servers.Where(s => - string.Equals(channelId, s.Profile.DiscordChannelId, StringComparison.OrdinalIgnoreCase) - && ( - string.Equals(profileIdOrAlias, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase) - || !string.IsNullOrWhiteSpace(s.Profile.DiscordAlias) && string.Equals(profileIdOrAlias, s.Profile.DiscordAlias, StringComparison.OrdinalIgnoreCase) - || !string.IsNullOrWhiteSpace(Config.Default.DiscordBotAllServersKeyword) && string.Equals(profileIdOrAlias, Config.Default.DiscordBotAllServersKeyword, StringComparison.OrdinalIgnoreCase) - || s.Profile.AllowDiscordClusterAlias && string.Equals(profileIdOrAlias, s.Profile.CrossArkClusterId, StringComparison.OrdinalIgnoreCase) - ) - ); - - if (serverList.IsEmpty()) - { - if (!string.IsNullOrWhiteSpace(Config.Default.DiscordBotAllServersKeyword) && string.Equals(profileIdOrAlias, Config.Default.DiscordBotAllServersKeyword, StringComparison.OrdinalIgnoreCase)) - { - responseList.Add(_globalizer.GetResourceString("DiscordBot_NoChannelProfiles")); - } - else - { - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileNotFound"), profileIdOrAlias)); - } - } - else - { - foreach (var server in serverList) - { - if (!server.Profile.AllowDiscordShutdown) - { - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Shutdown, server.Profile.ProfileName)); - continue; - } - - // check if another command is being run against the profile - if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) - { - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); - continue; - } - - switch (server.Runtime.Status) - { - case ServerStatus.Initializing: - case ServerStatus.Stopping: - case ServerStatus.Stopped: - case ServerStatus.Uninstalled: - case ServerStatus.Unknown: - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); - continue; - - case ServerStatus.Updating: - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); - continue; - } - - _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Shutdown); - profileList.Add(ServerProfileSnapshot.Create(server.Profile)); - } - } - }).Wait(token); - - foreach (var profile in profileList) - { - var app = new ServerApp(true) - { - DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, - OutputLogs = false, - SendAlerts = true, - SendEmails = false, - ServerProcess = ServerProcessType.Shutdown, - ServerStatusChangeCallback = (ServerStatus serverStatus) => - { - TaskUtils.RunOnUIThreadAsync(() => - { - var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); - if (server != null) - { - server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); - } - }).Wait(token); - } - }; - - Task.Run(() => - { - app.PerformProfileShutdown(profile, false, false, false, false, token); - _currentProfileCommands.Remove(profile.ProfileId); - }, token); - - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); - } - - return responseList; - } - - private static IList StopServer(string channelId, string profileIdOrAlias, CancellationToken token) + private static IList StopServer(string channelId, string profileIdOrAlias, bool shutdown, CancellationToken token) { if (string.IsNullOrWhiteSpace(profileIdOrAlias)) { @@ -655,12 +550,13 @@ namespace ServerManagerTool.Utils { var app = new ServerApp(true) { + BackupWorldFile = shutdown, DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, OutputLogs = false, + PerformWorldSave = shutdown, SendAlerts = true, SendEmails = false, - ServerProcess = ServerProcessType.Stop, - ShutdownInterval = 0, + ServerProcess = shutdown ? ServerProcessType.Shutdown : ServerProcessType.Stop, ServerStatusChangeCallback = (ServerStatus serverStatus) => { TaskUtils.RunOnUIThreadAsync(() => @@ -674,13 +570,19 @@ namespace ServerManagerTool.Utils } }; + if (!shutdown) + app.ShutdownInterval = 0; + Task.Run(() => { app.PerformProfileShutdown(profile, false, false, false, false, token); _currentProfileCommands.Remove(profile.ProfileId); }, token); - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); + if (shutdown) + responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); } return responseList; diff --git a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml index d2791ff9..48bc10d1 100644 --- a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml +++ b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml @@ -4,8 +4,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tb="http://www.hardcodet.net/taskbar" + xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" + xmlns:sm="clr-namespace:ServerManagerTool" xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common" xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common" + xmlns:controls="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common" xmlns:enum="clr-namespace:ServerManagerTool.Enums" xmlns:vm="clr-namespace:ServerManagerTool.Lib.ViewModel" mc:Ignorable="d" @@ -18,21 +21,38 @@ - + + + + + + + + + - + - + + + - + + + + + + + + @@ -41,12 +61,55 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - + @@ -544,7 +631,7 @@ - + @@ -557,7 +644,7 @@ Visibility="{Binding IsStandAloneWindow, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTipText="{Binding Title}" IconSource="../Art/favicon.ico" - LeftClickCommand="{Binding ShowWindowCommand, ElementName=ServerMonitorUI}"> + LeftClickCommand="{Binding ShowWindowCommand, ElementName=ServerMonitorUI}" Grid.ColumnSpan="2"> diff --git a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs index f23ac64e..17faf776 100644 --- a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs +++ b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs @@ -2,6 +2,7 @@ using NLog; using ServerManagerTool.Common.Lib; using ServerManagerTool.Common.Utils; +using ServerManagerTool.DiscordBot.Enums; using ServerManagerTool.Enums; using ServerManagerTool.Lib; using ServerManagerTool.Plugin.Common; @@ -16,7 +17,9 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Media; using WPFSharp.Globalizer; namespace ServerManagerTool.Windows @@ -26,17 +29,40 @@ namespace ServerManagerTool.Windows /// public partial class ServerMonitorWindow : Window { + public class ServerMonitorOutput_Error : Run + { + public ServerMonitorOutput_Error(string value) + : base(value) + { + Foreground = Brushes.Red; + } + } + + public class ServerMonitorOutput_Success : Run + { + public ServerMonitorOutput_Success(string value) + : base(value) + { + Foreground = Brushes.Green; + } + } + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly List Windows = new List(); private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance; private CancellationTokenSource _upgradeCancellationSource = null; private ActionQueue _versionChecker; + private ActionQueue _canExecuteChecker; + private readonly Dictionary _currentProfileCommands = new Dictionary(); + + private bool HasRunningCommands => _currentProfileCommands.Count > 0; public static readonly DependencyProperty ServerManagerProperty = DependencyProperty.Register(nameof(ServerManager), typeof(ServerManager), typeof(ServerMonitorWindow), new PropertyMetadata(null)); public static readonly DependencyProperty LatestServerManagerVersionProperty = DependencyProperty.Register(nameof(LatestServerManagerVersion), typeof(Version), typeof(ServerMonitorWindow), new PropertyMetadata(new Version())); public static readonly DependencyProperty ShowUpdateButtonProperty = DependencyProperty.Register(nameof(ShowUpdateButton), typeof(bool), typeof(ServerMonitorWindow), new PropertyMetadata(false)); public static readonly DependencyProperty IsStandAloneWindowProperty = DependencyProperty.Register(nameof(IsStandAloneWindow), typeof(bool), typeof(ServerMonitorWindow), new PropertyMetadata(false)); + public static readonly DependencyProperty CancellationTokenSourceProperty = DependencyProperty.Register(nameof(CancellationTokenSource), typeof(CancellationTokenSource), typeof(ServerMonitorWindow)); public ServerMonitorWindow() : this(null) { @@ -89,6 +115,12 @@ namespace ServerManagerTool.Windows set { SetValue(IsStandAloneWindowProperty, value); } } + public CancellationTokenSource CancellationTokenSource + { + get { return (CancellationTokenSource)GetValue(CancellationTokenSourceProperty); } + set { SetValue(CancellationTokenSourceProperty, value); } + } + private void ServerMonitorWindow_Loaded(object sender, RoutedEventArgs e) { if (ServerManager == null) @@ -121,6 +153,9 @@ namespace ServerManagerTool.Windows _versionChecker = new ActionQueue(); _versionChecker.PostAction(CheckForUpdates).DoNotWait(); } + + _canExecuteChecker = new ActionQueue(); + _canExecuteChecker.PostAction(RaiseCanExecuteChanged).DoNotWait(); } private void ServerMonitorWindow_LocationChanged(object sender, EventArgs e) @@ -156,6 +191,14 @@ namespace ServerManagerTool.Windows protected override void OnClosing(CancelEventArgs e) { + if (HasRunningCommands) + { + MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_RunningProcesses_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_RunningProcesses_ConfirmTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + + e.Cancel = true; + return; + } + if (this.OwnedWindows.OfType().Any()) { if (MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_CloseWindow_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_CloseWindow_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes) @@ -166,7 +209,9 @@ namespace ServerManagerTool.Windows } Windows.Remove(this); + _versionChecker?.DisposeAsync().DoNotWait(); + _canExecuteChecker?.DisposeAsync().DoNotWait(); base.OnClosing(e); } @@ -409,6 +454,29 @@ namespace ServerManagerTool.Windows } } + public void AddErrorBlockContent(string message) + { + var p = new Paragraph(); + + p.Inlines.Add(new ServerMonitorOutput_Error(message)); + + ConsoleContent.Blocks.Add(p); + } + + public void AddMessageBlockContent(string message) + { + var p = new Paragraph(); + + p.Inlines.Add(new ServerMonitorOutput_Success(message)); + + ConsoleContent.Blocks.Add(p); + } + + public void ClearBlockContents() + { + ConsoleContent.Blocks.Clear(); + } + private async Task CheckForUpdates() { string url = App.Instance.BetaVersion ? Config.Default.LatestASMBetaVersionUrl : Config.Default.LatestASMVersionUrl; @@ -455,6 +523,14 @@ namespace ServerManagerTool.Windows return new ServerMonitorWindow(serverManager); } + public async Task RaiseCanExecuteChanged() + { + await TaskUtils.RunOnUIThreadAsync(() => CommandManager.InvalidateRequerySuggested()); + await Task.Delay(5000); + + _canExecuteChecker?.PostAction(RaiseCanExecuteChanged).DoNotWait(); + } + private void SetWindowTitle() { if (!string.IsNullOrWhiteSpace(App.Instance.Title)) @@ -702,6 +778,114 @@ namespace ServerManagerTool.Windows } } + public ICommand BackupServersCommand + { + get + { + return new RelayCommand( + execute: async (_) => + { + await BackupSelectedServersAsync(); + }, + canExecute: (_) => + { + return ServerManager?.Servers != null && ServerManager.Servers.Count > 0 && ServerManager.Servers.Any(s => s.Selected) && ServerManager.Servers.All(s => s.Runtime.Status != ServerStatus.Unknown) + && CancellationTokenSource == null; + } + ); + } + } + + public ICommand RestartServersCommand + { + get + { + return new RelayCommand( + execute: async (_) => + { + await RestartSelectedServersAsync(); + }, + canExecute: (_) => + { + return ServerManager?.Servers != null && ServerManager.Servers.Count > 0 && ServerManager.Servers.Any(s => s.Selected) && ServerManager.Servers.All(s => s.Runtime.Status != ServerStatus.Unknown) + && CancellationTokenSource == null; + } + ); + } + } + + public ICommand ShutdownServersCommand + { + get + { + return new RelayCommand( + execute: async (_) => + { + await StopSelectedServersAsync(true); + }, + canExecute: (_) => + { + return ServerManager?.Servers != null && ServerManager.Servers.Count > 0 && ServerManager.Servers.Any(s => s.Selected) && ServerManager.Servers.All(s => s.Runtime.Status != ServerStatus.Unknown) + && CancellationTokenSource == null; + } + ); + } + } + + public ICommand StartServersCommand + { + get + { + return new RelayCommand( + execute: async (_) => + { + await StartSelectedServersAsync(); + }, + canExecute: (_) => + { + return ServerManager?.Servers != null && ServerManager.Servers.Count > 0 && ServerManager.Servers.Any(s => s.Selected) && ServerManager.Servers.All(s => s.Runtime.Status != ServerStatus.Unknown) + && CancellationTokenSource == null; + } + ); + } + } + + public ICommand StopServersCommand + { + get + { + return new RelayCommand( + execute: async (_) => + { + await StopSelectedServersAsync(false); + }, + canExecute: (_) => + { + return ServerManager?.Servers != null && ServerManager.Servers.Count > 0 && ServerManager.Servers.Any(s => s.Selected) && ServerManager.Servers.All(s => s.Runtime.Status != ServerStatus.Unknown) + && CancellationTokenSource == null; + } + ); + } + } + + public ICommand UpdateServersCommand + { + get + { + return new RelayCommand( + execute: async (_) => + { + await UpdateSelectedServersAsync(); + }, + canExecute: (_) => + { + return ServerManager?.Servers != null && ServerManager.Servers.Count > 0 && ServerManager.Servers.Any(s => s.Selected) && ServerManager.Servers.All(s => s.Runtime.Status != ServerStatus.Unknown) + && CancellationTokenSource == null; + } + ); + } + } + #region Drag and Drop public static readonly DependencyProperty DraggedItemProperty = DependencyProperty.Register(nameof(DraggedItem), typeof(Server), typeof(ServerMonitorWindow), new PropertyMetadata(null)); @@ -816,5 +1000,521 @@ namespace ServerManagerTool.Windows } #endregion + + private void SelectAllServers_Click(object sender, RoutedEventArgs e) + { + foreach (var server in ServerManager.Servers) + { + server.Selected = true; + } + } + + private void UnselectAllServers_Click(object sender, RoutedEventArgs e) + { + foreach (var server in ServerManager.Servers) + { + server.Selected = false; + } + } + + private async Task BackupSelectedServersAsync() + { + if (CancellationTokenSource != null) + return; + + var serverList = ServerManager.Servers.Where(s => s.Selected); + if (serverList.IsEmpty()) + { + MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_BackupServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_BackupServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + return; + + ClearBlockContents(); + + var profileList = new List(); + + foreach (var server in serverList) + { + // check if another command is being run against the profile + if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); + continue; + } + + switch (server.Runtime.Status) + { + case ServerStatus.Initializing: + case ServerStatus.Stopping: + case ServerStatus.Uninstalled: + case ServerStatus.Unknown: + case ServerStatus.Updating: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + } + + _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Backup); + profileList.Add(ServerProfileSnapshot.Create(server.Profile)); + } + + CancellationTokenSource = new CancellationTokenSource(); + var token = CancellationTokenSource.Token; + var tasks = new List(); + + foreach (var profile in profileList) + { + var app = new ServerApp(true) + { + DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, + OutputLogs = false, + SendAlerts = true, + SendEmails = false, + ServerProcess = ServerProcessType.Backup, + ServerStatusChangeCallback = (ServerStatus serverStatus) => + { + TaskUtils.RunOnUIThreadAsync(() => + { + var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); + if (server != null) + { + server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); + } + }).Wait(token); + } + }; + + var task = Task.Run(() => + { + app.PerformProfileBackup(profile, token); + _currentProfileCommands.Remove(profile.ProfileId); + }, token); + + tasks.Add(task); + + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_BackupRequested"), profile.ServerName)); + } + + try + { + await Task.WhenAll(tasks); + } + catch { } + finally + { + CancellationTokenSource?.Dispose(); + CancellationTokenSource = null; + } + } + + private async Task RestartSelectedServersAsync() + { + if (CancellationTokenSource != null) + return; + + var serverList = ServerManager.Servers.Where(s => s.Selected); + if (serverList.IsEmpty()) + { + MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_RestartServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_RestartServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + return; + + ClearBlockContents(); + + var profileList = new List(); + + foreach (var server in serverList) + { + // check if another command is being run against the profile + if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); + continue; + } + + switch (server.Runtime.Status) + { + case ServerStatus.Initializing: + case ServerStatus.Stopping: + case ServerStatus.Uninstalled: + case ServerStatus.Unknown: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + + case ServerStatus.Updating: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); + continue; + } + + _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Restart); + var profile = ServerProfileSnapshot.Create(server.Profile); + profile.AutoRestartIfShutdown = true; + profileList.Add(profile); + } + + CancellationTokenSource = new CancellationTokenSource(); + var token = CancellationTokenSource.Token; + var tasks = new List(); + + foreach (var profile in profileList) + { + var app = new ServerApp(true) + { + DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, + OutputLogs = false, + SendAlerts = true, + SendEmails = false, + ServerProcess = ServerProcessType.Restart, + ServerStatusChangeCallback = (ServerStatus serverStatus) => + { + TaskUtils.RunOnUIThreadAsync(() => + { + var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); + if (server != null) + { + server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); + } + }).Wait(token); + } + }; + + var task = Task.Run(() => + { + app.PerformProfileShutdown(profile, true, false, false, false, token); + _currentProfileCommands.Remove(profile.ProfileId); + }, token); + + tasks.Add(task); + + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName)); + } + + try + { + await Task.WhenAll(tasks); + } + catch { } + finally + { + CancellationTokenSource?.Dispose(); + CancellationTokenSource = null; + } + } + + private async Task StartSelectedServersAsync() + { + if (CancellationTokenSource != null) + return; + + var serverList = ServerManager.Servers.Where(s => s.Selected); + if (serverList.IsEmpty()) + { + MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_StartServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_StartServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + return; + + ClearBlockContents(); + + var profileList = new List(); + + foreach (var server in serverList) + { + // check if another command is being run against the profile + if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); + continue; + } + + switch (server.Runtime.Status) + { + case ServerStatus.Initializing: + case ServerStatus.Stopping: + case ServerStatus.Running: + case ServerStatus.Uninstalled: + case ServerStatus.Unknown: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + + case ServerStatus.Updating: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); + continue; + } + + _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Start); + var profile = ServerProfileSnapshot.Create(server.Profile); + profile.AutoRestartIfShutdown = true; + profileList.Add(profile); + } + + CancellationTokenSource = new CancellationTokenSource(); + var token = CancellationTokenSource.Token; + var tasks = new List(); + + foreach (var profile in profileList) + { + var app = new ServerApp(true) + { + DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, + OutputLogs = false, + SendAlerts = true, + SendEmails = false, + ServerProcess = ServerProcessType.Restart, + ServerStatusChangeCallback = (ServerStatus serverStatus) => + { + TaskUtils.RunOnUIThreadAsync(() => + { + var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); + if (server != null) + { + server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); + } + }).Wait(token); + } + }; + + var task = Task.Run(() => + { + app.PerformProfileShutdown(profile, true, false, false, false, token); + _currentProfileCommands.Remove(profile.ProfileId); + }, token); + + tasks.Add(task); + + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName)); + } + + try + { + await Task.WhenAll(tasks); + } + catch { } + finally + { + CancellationTokenSource?.Dispose(); + CancellationTokenSource = null; + } + } + + private async Task StopSelectedServersAsync(bool shutdown) + { + if (CancellationTokenSource != null) + return; + + var serverList = ServerManager.Servers.Where(s => s.Selected); + if (serverList.IsEmpty()) + { + MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + var result = shutdown + ? MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_ShutdownServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_ShutdownServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) + : MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + return; + + ClearBlockContents(); + + var profileList = new List(); + + foreach (var server in serverList) + { + // check if another command is being run against the profile + if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); + continue; + } + + switch (server.Runtime.Status) + { + case ServerStatus.Initializing: + case ServerStatus.Stopping: + case ServerStatus.Stopped: + case ServerStatus.Uninstalled: + case ServerStatus.Unknown: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + + case ServerStatus.Updating: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); + continue; + } + + _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Stop); + profileList.Add(ServerProfileSnapshot.Create(server.Profile)); + } + + CancellationTokenSource = new CancellationTokenSource(); + var token = CancellationTokenSource.Token; + var tasks = new List(); + + foreach (var profile in profileList) + { + var app = new ServerApp(true) + { + BackupWorldFile = shutdown, + DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, + OutputLogs = false, + PerformWorldSave = shutdown, + SendAlerts = true, + SendEmails = false, + ServerProcess = shutdown ? ServerProcessType.Shutdown : ServerProcessType.Stop, + ServerStatusChangeCallback = (ServerStatus serverStatus) => + { + TaskUtils.RunOnUIThreadAsync(() => + { + var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); + if (server != null) + { + server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); + } + }).Wait(token); + } + }; + + if (!shutdown) + app.ShutdownInterval = 0; + + var task = Task.Run(() => + { + app.PerformProfileShutdown(profile, false, false, false, false, token); + _currentProfileCommands.Remove(profile.ProfileId); + }, token); + + tasks.Add(task); + + if (shutdown) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); + } + + try + { + await Task.WhenAll(tasks); + } + catch { } + finally + { + CancellationTokenSource?.Dispose(); + CancellationTokenSource = null; + } + } + + private async Task UpdateSelectedServersAsync() + { + if (CancellationTokenSource != null) + return; + + var serverList = ServerManager.Servers.Where(s => s.Selected); + if (serverList.IsEmpty()) + { + MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_UpdateServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_UpdateServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result != MessageBoxResult.Yes) + return; + + ClearBlockContents(); + + var profileList = new List(); + + foreach (var server in serverList) + { + var performRestart = false; + + // check if another command is being run against the profile + if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) + { + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); + continue; + } + + switch (server.Runtime.Status) + { + case ServerStatus.Running: + performRestart = true; + break; + + case ServerStatus.Initializing: + case ServerStatus.Stopping: + case ServerStatus.Unknown: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); + continue; + + case ServerStatus.Updating: + AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); + continue; + } + + _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Update); + var profile = ServerProfileSnapshot.Create(server.Profile); + profile.RestartAfterShutdown1 = performRestart; // use this property to trigger a restart + profileList.Add(profile); + } + + CancellationTokenSource = new CancellationTokenSource(); + var token = CancellationTokenSource.Token; + var tasks = new List(); + + foreach (var profile in profileList) + { + var app = new ServerApp(true) + { + DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, + OutputLogs = false, + SendAlerts = true, + SendEmails = false, + ServerProcess = ServerProcessType.Update, + ServerStatusChangeCallback = (ServerStatus serverStatus) => + { + TaskUtils.RunOnUIThreadAsync(() => + { + var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); + if (server != null) + { + server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); + } + }).Wait(token); + } + }; + + var task = Task.Run(() => + { + app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, true, false, false, token); + _currentProfileCommands.Remove(profile.ProfileId); + }, token); + + tasks.Add(task); + + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_UpdateRequested"), profile.ServerName)); + } + + try + { + await Task.WhenAll(tasks); + } + catch { } + finally + { + CancellationTokenSource?.Dispose(); + CancellationTokenSource = null; + } + } } } diff --git a/src/ConanServerManager.Common/ConanServerManager.Common.csproj b/src/ConanServerManager.Common/ConanServerManager.Common.csproj index f932d07f..8e7de9be 100644 --- a/src/ConanServerManager.Common/ConanServerManager.Common.csproj +++ b/src/ConanServerManager.Common/ConanServerManager.Common.csproj @@ -1,6 +1,6 @@  - Debug;Release;Debug - Beta + Debug;Release net462 diff --git a/src/ConanServerManager/Art/Restart.ico b/src/ConanServerManager/Art/Restart.ico new file mode 100644 index 00000000..3e07dd1e Binary files /dev/null and b/src/ConanServerManager/Art/Restart.ico differ diff --git a/src/ConanServerManager/ConanServerManager.csproj b/src/ConanServerManager/ConanServerManager.csproj index d37b3eca..e1053835 100644 --- a/src/ConanServerManager/ConanServerManager.csproj +++ b/src/ConanServerManager/ConanServerManager.csproj @@ -123,6 +123,16 @@ false + + true + bin\Debug - ServerMonitor\ + DEBUG;TRACE + full + AnyCPU + 7.3 + prompt + true + ..\packages\DotNetZip.1.13.8\lib\net40\DotNetZip.dll @@ -287,6 +297,7 @@ + Designer PreserveNewest diff --git a/src/ConanServerManager/Globalization/en-US/en-US.xaml b/src/ConanServerManager/Globalization/en-US/en-US.xaml index 36c7d163..eac1a72c 100644 --- a/src/ConanServerManager/Globalization/en-US/en-US.xaml +++ b/src/ConanServerManager/Globalization/en-US/en-US.xaml @@ -400,6 +400,7 @@ Server Monitor + Selected Total Servers: Server Map @@ -409,6 +410,16 @@ Status Create a desktop shortcut to open this form directly. + Start the selected servers. + Shutdown Selected Servers + Shutdown the selected servers. + Stop Selected Servers + Stop the selected servers. + Restart the selected servers. + Update the selected servers. + Backup the selected servers. + Select all servers. + Unselect all servers. Open the Player List window. Open the RCON window. Start the server. @@ -419,7 +430,23 @@ Server Update Error Another server is being upgraded, wait until the upgrade has finished and try again. Confirm Window Close - You are currently perform a server update, closing the window with disconnect you from steamcmd. Do you want to continue closing the window? + You are currently performing a server update, closing the window will disconnect you from steamcmd. Do you want to continue closing the window? + Confirm Shutdown Servers + You are about to shutdown the selected servers. Do you want to continue? + Confirm Start Servers + You are about to start the selected servers. Do you want to continue? + Confirm Stop Servers + You are about to stop the selected servers. Do you want to continue? + Confirm Restart Servers + You are about to restart the selected servers. Do you want to continue? + Confirm Update Servers + You are about to update the selected servers. Do you want to continue? + Confirm Backup Servers + You are about to backup the selected servers. Do you want to continue? + Selected Servers Error + You have not selected any servers. Selected one or more servers in the list and try again. + Close Server Monitor Error + The server monitor window cannot be closed at this time. One or more of the servers is currently starting, shutting down or restarting. diff --git a/src/ConanServerManager/Lib/ServerApp.cs b/src/ConanServerManager/Lib/ServerApp.cs index f5970635..efe4a395 100644 --- a/src/ConanServerManager/Lib/ServerApp.cs +++ b/src/ConanServerManager/Lib/ServerApp.cs @@ -102,6 +102,7 @@ namespace ServerManagerTool.Lib public bool DeleteOldBackupFiles = Config.Default.AutoBackup_DeleteOldFiles; public int ExitCode = EXITCODE_NORMALEXIT; public bool OutputLogs = false; + public bool PerformWorldSave = Config.Default.ServerShutdown_EnableWorldSave; public bool SendAlerts = false; public bool SendEmails = false; public string ShutdownReason = null; @@ -258,11 +259,14 @@ namespace ServerManagerTool.Lib ServerStatusChangeCallback?.Invoke(ServerStatus.Stopped); - // make a backup of the current profile and config files. - CreateProfileBackupArchiveFile(_profile); + if (ServerProcess != ServerProcessType.Stop) + { + // make a backup of the current profile and config files. + CreateProfileBackupArchiveFile(_profile); - if (ExitCode != EXITCODE_NORMALEXIT) - return; + if (ExitCode != EXITCODE_NORMALEXIT) + return; + } if (BackupWorldFile) { @@ -285,10 +289,10 @@ namespace ServerManagerTool.Lib { ServerStatusChangeCallback?.Invoke(ServerStatus.Stopped); } - } - if (ExitCode != EXITCODE_NORMALEXIT) - return; + if (ExitCode != EXITCODE_NORMALEXIT) + return; + } // check if this is a shutdown only, or a shutdown and restart. if (restartServer) @@ -579,7 +583,7 @@ namespace ServerManagerTool.Lib // BH - commented out until funcom provide a way to send a save command // check if we need to perform a world save - //if (serverAccessible && Config.Default.ServerShutdown_EnableWorldSave) + //if (serverAccessible && PerformWorldSave) //{ // try // { @@ -716,6 +720,7 @@ namespace ServerManagerTool.Lib if (process.HasExited) { process.Close(); + if (Config.Default.EmailNotify_ShutdownRestart) SendEmail($"{_profile.ProfileName} server shutdown", $"The server has been shutdown to perform the {ServerProcess} process.", false); } diff --git a/src/ConanServerManager/Utils/DiscordBotHelper.cs b/src/ConanServerManager/Utils/DiscordBotHelper.cs index 49994593..036b9ea6 100644 --- a/src/ConanServerManager/Utils/DiscordBotHelper.cs +++ b/src/ConanServerManager/Utils/DiscordBotHelper.cs @@ -58,11 +58,11 @@ namespace ServerManagerTool.Utils return new List { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) }; case CommandType.Shutdown: if (Config.Default.AllowDiscordShutdown) - return ShutdownServer(channelId, profileIdOrAlias, token); + return StopServer(channelId, profileIdOrAlias, true, token); return new List { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) }; case CommandType.Stop: if (Config.Default.AllowDiscordStop) - return StopServer(channelId, profileIdOrAlias, token); + return StopServer(channelId, profileIdOrAlias, false, token); return new List { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) }; case CommandType.Start: if (Config.Default.AllowDiscordStart) @@ -471,111 +471,7 @@ namespace ServerManagerTool.Utils return responseList; } - private static IList ShutdownServer(string channelId, string profileIdOrAlias, CancellationToken token) - { - if (string.IsNullOrWhiteSpace(profileIdOrAlias)) - { - return new List { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), CommandType.Shutdown) }; - } - - var profileList = new List(); - var responseList = new List(); - - TaskUtils.RunOnUIThreadAsync(() => - { - var serverList = ServerManager.Instance.Servers.Where(s => - string.Equals(channelId, s.Profile.DiscordChannelId, StringComparison.OrdinalIgnoreCase) - && ( - string.Equals(profileIdOrAlias, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase) - || !string.IsNullOrWhiteSpace(s.Profile.DiscordAlias) && string.Equals(profileIdOrAlias, s.Profile.DiscordAlias, StringComparison.OrdinalIgnoreCase) - || !string.IsNullOrWhiteSpace(Config.Default.DiscordBotAllServersKeyword) && string.Equals(profileIdOrAlias, Config.Default.DiscordBotAllServersKeyword, StringComparison.OrdinalIgnoreCase) - ) - ); - - if (serverList.IsEmpty()) - { - if (!string.IsNullOrWhiteSpace(Config.Default.DiscordBotAllServersKeyword) && string.Equals(profileIdOrAlias, Config.Default.DiscordBotAllServersKeyword, StringComparison.OrdinalIgnoreCase)) - { - responseList.Add(_globalizer.GetResourceString("DiscordBot_NoChannelProfiles")); - } - else - { - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileNotFound"), profileIdOrAlias)); - } - } - else - { - foreach (var server in serverList) - { - if (!server.Profile.AllowDiscordShutdown) - { - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Shutdown, server.Profile.ProfileName)); - continue; - } - - // check if another command is being run against the profile - if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID)) - { - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName)); - continue; - } - - switch (server.Runtime.Status) - { - case ServerStatus.Initializing: - case ServerStatus.Stopping: - case ServerStatus.Stopped: - case ServerStatus.Uninstalled: - case ServerStatus.Unknown: - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString)); - continue; - - case ServerStatus.Updating: - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName)); - continue; - } - - _currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Shutdown); - profileList.Add(ServerProfileSnapshot.Create(server.Profile)); - } - } - }).Wait(token); - - foreach (var profile in profileList) - { - var app = new ServerApp(true) - { - DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, - OutputLogs = false, - SendAlerts = true, - SendEmails = false, - ServerProcess = ServerProcessType.Shutdown, - ServerStatusChangeCallback = (ServerStatus serverStatus) => - { - TaskUtils.RunOnUIThreadAsync(() => - { - var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase)); - if (server != null) - { - server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown); - } - }).Wait(token); - } - }; - - Task.Run(() => - { - app.PerformProfileShutdown(profile, false, false, false, false, token); - _currentProfileCommands.Remove(profile.ProfileId); - }, token); - - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); - } - - return responseList; - } - - private static IList StopServer(string channelId, string profileIdOrAlias, CancellationToken token) + private static IList StopServer(string channelId, string profileIdOrAlias, bool shutdown, CancellationToken token) { if (string.IsNullOrWhiteSpace(profileIdOrAlias)) { @@ -649,12 +545,13 @@ namespace ServerManagerTool.Utils { var app = new ServerApp(true) { + BackupWorldFile = shutdown, DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, OutputLogs = false, + PerformWorldSave = shutdown, SendAlerts = true, SendEmails = false, - ServerProcess = ServerProcessType.Stop, - ShutdownInterval = 0, + ServerProcess = shutdown ? ServerProcessType.Shutdown : ServerProcessType.Stop, ServerStatusChangeCallback = (ServerStatus serverStatus) => { TaskUtils.RunOnUIThreadAsync(() => @@ -668,13 +565,19 @@ namespace ServerManagerTool.Utils } }; + if (!shutdown) + app.ShutdownInterval = 0; + Task.Run(() => { app.PerformProfileShutdown(profile, false, false, false, false, token); _currentProfileCommands.Remove(profile.ProfileId); }, token); - responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); + if (shutdown) + responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); } return responseList; diff --git a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml index 8d28adf1..7721108b 100644 --- a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml +++ b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml @@ -4,8 +4,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tb="http://www.hardcodet.net/taskbar" + xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" + xmlns:sm="clr-namespace:ServerManagerTool" xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common" xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common" + xmlns:controls="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common" xmlns:enum="clr-namespace:ServerManagerTool.Enums" xmlns:vm="clr-namespace:ServerManagerTool.Lib.ViewModel" mc:Ignorable="d" @@ -19,7 +22,15 @@ + + + + + + + + @@ -28,11 +39,20 @@ - + + + - + + + + + + + + @@ -41,12 +61,55 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -