From 61991ac55f0f75c4658402953d0f7908a102a041 Mon Sep 17 00:00:00 2001 From: Brett Hewitson Date: Wed, 18 May 2022 01:01:44 +1000 Subject: [PATCH] Server Monitor Changes - removed old button click events and replaced with commands. - removed shutdown methods and added shutdown parameter to stop methods. --- .../Globalization/en-US/en-US.xaml | 5 + src/ARKServerManager/Lib/ServerApp.cs | 67 ++++--- .../Utils/DiscordBotHelper.cs | 124 ++---------- .../Windows/ServerMonitorWindow.xaml | 123 +++++------- .../Windows/ServerMonitorWindow.xaml.cs | 180 +++++++++++++++--- .../Globalization/en-US/en-US.xaml | 5 + src/ConanServerManager/Lib/ServerApp.cs | 21 +- .../Utils/DiscordBotHelper.cs | 123 ++---------- .../Windows/ServerMonitorWindow.xaml | 123 +++++------- .../Windows/ServerMonitorWindow.xaml.cs | 180 +++++++++++++++--- src/ServerManager.Common/Lib/ActionQueue.cs | 1 - src/ServerManager.Common/Lib/RelayCommand.cs | 7 +- 12 files changed, 490 insertions(+), 469 deletions(-) diff --git a/src/ARKServerManager/Globalization/en-US/en-US.xaml b/src/ARKServerManager/Globalization/en-US/en-US.xaml index c2a65f54..597c4289 100644 --- a/src/ARKServerManager/Globalization/en-US/en-US.xaml +++ b/src/ARKServerManager/Globalization/en-US/en-US.xaml @@ -451,6 +451,9 @@ 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. @@ -468,6 +471,8 @@ Another server is being upgraded, wait until the upgrade has finished and try again. Confirm Window Close 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 diff --git a/src/ARKServerManager/Lib/ServerApp.cs b/src/ARKServerManager/Lib/ServerApp.cs index 24790303..7f8af24f 100644 --- a/src/ARKServerManager/Lib/ServerApp.cs +++ b/src/ARKServerManager/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) @@ -580,7 +584,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 { @@ -671,35 +675,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 95ff243c..48bc10d1 100644 --- a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml +++ b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml @@ -6,9 +6,9 @@ xmlns:tb="http://www.hardcodet.net/taskbar" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:sm="clr-namespace:ServerManagerTool" - xmlns:smw="clr-namespace:ServerManagerTool.Windows" 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" @@ -24,6 +24,13 @@ + + + + + + + @@ -54,89 +61,49 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs index 34dd543b..17faf776 100644 --- a/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs +++ b/src/ARKServerManager/Windows/ServerMonitorWindow.xaml.cs @@ -53,6 +53,7 @@ namespace ServerManagerTool.Windows 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; @@ -152,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) @@ -205,7 +209,9 @@ namespace ServerManagerTool.Windows } Windows.Remove(this); + _versionChecker?.DisposeAsync().DoNotWait(); + _canExecuteChecker?.DisposeAsync().DoNotWait(); base.OnClosing(e); } @@ -517,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)) @@ -764,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)); @@ -879,7 +1001,23 @@ namespace ServerManagerTool.Windows #endregion - private async void BackupServers_Click(object sender, RoutedEventArgs e) + 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; @@ -972,7 +1110,7 @@ namespace ServerManagerTool.Windows } } - private async void RestartServers_Click(object sender, RoutedEventArgs e) + private async Task RestartSelectedServersAsync() { if (CancellationTokenSource != null) return; @@ -1070,7 +1208,7 @@ namespace ServerManagerTool.Windows } } - private async void StartServers_Click(object sender, RoutedEventArgs e) + private async Task StartSelectedServersAsync() { if (CancellationTokenSource != null) return; @@ -1169,7 +1307,7 @@ namespace ServerManagerTool.Windows } } - private async void StopServers_Click(object sender, RoutedEventArgs e) + private async Task StopSelectedServersAsync(bool shutdown) { if (CancellationTokenSource != null) return; @@ -1181,7 +1319,9 @@ namespace ServerManagerTool.Windows return; } - var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + 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; @@ -1225,11 +1365,13 @@ namespace ServerManagerTool.Windows { var app = new ServerApp(true) { + BackupWorldFile = shutdown, DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, OutputLogs = false, + PerformWorldSave = shutdown, SendAlerts = true, SendEmails = false, - ServerProcess = ServerProcessType.Shutdown, + ServerProcess = shutdown ? ServerProcessType.Shutdown : ServerProcessType.Stop, ServerStatusChangeCallback = (ServerStatus serverStatus) => { TaskUtils.RunOnUIThreadAsync(() => @@ -1243,6 +1385,9 @@ namespace ServerManagerTool.Windows } }; + if (!shutdown) + app.ShutdownInterval = 0; + var task = Task.Run(() => { app.PerformProfileShutdown(profile, false, false, false, false, token); @@ -1251,7 +1396,10 @@ namespace ServerManagerTool.Windows tasks.Add(task); - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + if (shutdown) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); } try @@ -1266,7 +1414,7 @@ namespace ServerManagerTool.Windows } } - private async void UpdateServers_Click(object sender, RoutedEventArgs e) + private async Task UpdateSelectedServersAsync() { if (CancellationTokenSource != null) return; @@ -1368,21 +1516,5 @@ namespace ServerManagerTool.Windows CancellationTokenSource = null; } } - - 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; - } - } } } diff --git a/src/ConanServerManager/Globalization/en-US/en-US.xaml b/src/ConanServerManager/Globalization/en-US/en-US.xaml index a55a7174..eac1a72c 100644 --- a/src/ConanServerManager/Globalization/en-US/en-US.xaml +++ b/src/ConanServerManager/Globalization/en-US/en-US.xaml @@ -411,6 +411,9 @@ 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. @@ -428,6 +431,8 @@ Another server is being upgraded, wait until the upgrade has finished and try again. Confirm Window Close 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 diff --git a/src/ConanServerManager/Lib/ServerApp.cs b/src/ConanServerManager/Lib/ServerApp.cs index 21b4ab81..482a0979 100644 --- a/src/ConanServerManager/Lib/ServerApp.cs +++ b/src/ConanServerManager/Lib/ServerApp.cs @@ -103,6 +103,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; @@ -259,11 +260,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) { @@ -286,10 +290,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) @@ -580,7 +584,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 // { @@ -717,6 +721,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 9c8b12f6..7721108b 100644 --- a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml +++ b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml @@ -6,9 +6,9 @@ xmlns:tb="http://www.hardcodet.net/taskbar" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:sm="clr-namespace:ServerManagerTool" - xmlns:smw="clr-namespace:ServerManagerTool.Windows" 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" @@ -24,6 +24,13 @@ + + + + + + + @@ -54,89 +61,49 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs index b2ee9381..2e57dec5 100644 --- a/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs +++ b/src/ConanServerManager/Windows/ServerMonitorWindow.xaml.cs @@ -53,6 +53,7 @@ namespace ServerManagerTool.Windows 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; @@ -152,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) @@ -205,7 +209,9 @@ namespace ServerManagerTool.Windows } Windows.Remove(this); + _versionChecker?.DisposeAsync().DoNotWait(); + _canExecuteChecker?.DisposeAsync().DoNotWait(); base.OnClosing(e); } @@ -517,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)) @@ -761,6 +775,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)); @@ -876,7 +998,23 @@ namespace ServerManagerTool.Windows #endregion - private async void BackupServers_Click(object sender, RoutedEventArgs e) + 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; @@ -969,7 +1107,7 @@ namespace ServerManagerTool.Windows } } - private async void RestartServers_Click(object sender, RoutedEventArgs e) + private async Task RestartSelectedServersAsync() { if (CancellationTokenSource != null) return; @@ -1067,7 +1205,7 @@ namespace ServerManagerTool.Windows } } - private async void StartServers_Click(object sender, RoutedEventArgs e) + private async Task StartSelectedServersAsync() { if (CancellationTokenSource != null) return; @@ -1166,7 +1304,7 @@ namespace ServerManagerTool.Windows } } - private async void StopServers_Click(object sender, RoutedEventArgs e) + private async Task StopSelectedServersAsync(bool shutdown) { if (CancellationTokenSource != null) return; @@ -1178,7 +1316,9 @@ namespace ServerManagerTool.Windows return; } - var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question); + 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; @@ -1222,11 +1362,13 @@ namespace ServerManagerTool.Windows { var app = new ServerApp(true) { + BackupWorldFile = shutdown, DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup, OutputLogs = false, + PerformWorldSave = shutdown, SendAlerts = true, SendEmails = false, - ServerProcess = ServerProcessType.Shutdown, + ServerProcess = shutdown ? ServerProcessType.Shutdown : ServerProcessType.Stop, ServerStatusChangeCallback = (ServerStatus serverStatus) => { TaskUtils.RunOnUIThreadAsync(() => @@ -1240,6 +1382,9 @@ namespace ServerManagerTool.Windows } }; + if (!shutdown) + app.ShutdownInterval = 0; + var task = Task.Run(() => { app.PerformProfileShutdown(profile, false, false, false, false, token); @@ -1248,7 +1393,10 @@ namespace ServerManagerTool.Windows tasks.Add(task); - AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + if (shutdown) + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName)); + else + AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName)); } try @@ -1263,7 +1411,7 @@ namespace ServerManagerTool.Windows } } - private async void UpdateServers_Click(object sender, RoutedEventArgs e) + private async Task UpdateSelectedServersAsync() { if (CancellationTokenSource != null) return; @@ -1365,21 +1513,5 @@ namespace ServerManagerTool.Windows CancellationTokenSource = null; } } - - 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; - } - } } } diff --git a/src/ServerManager.Common/Lib/ActionQueue.cs b/src/ServerManager.Common/Lib/ActionQueue.cs index 714c0092..aab396c4 100644 --- a/src/ServerManager.Common/Lib/ActionQueue.cs +++ b/src/ServerManager.Common/Lib/ActionQueue.cs @@ -19,7 +19,6 @@ namespace ServerManagerTool.Common.Lib public ActionQueue(TaskScheduler scheduler = null) { this.workQueue = new ActionBlock(a => a.Invoke(), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1, TaskScheduler = scheduler ?? TaskScheduler.Default }); - } public Task PostAction(Func action) diff --git a/src/ServerManager.Common/Lib/RelayCommand.cs b/src/ServerManager.Common/Lib/RelayCommand.cs index f65cd0e2..f2221418 100644 --- a/src/ServerManager.Common/Lib/RelayCommand.cs +++ b/src/ServerManager.Common/Lib/RelayCommand.cs @@ -31,10 +31,7 @@ namespace ServerManagerTool.Common.Lib /// The execution status logic. public RelayCommand(Action execute, Predicate canExecute) { - if (execute == null) - throw new ArgumentNullException("execute"); - - _execute = execute; + _execute = execute ?? throw new ArgumentNullException("execute"); _canExecute = canExecute; } @@ -53,7 +50,7 @@ namespace ServerManagerTool.Common.Lib { try { - return _canExecute == null ? true : _canExecute((T)parameter); + return _canExecute == null || _canExecute((T)parameter); } catch (Exception) {