Server Monitor Changes

- added sequential checkbox and slider (not implemented up yet)
- added separate mod only update option.
- added a cancel process option.
- a lot of changes with the process methods - consoladated stop/shutdown and start/restart.

- removed the server accessable flag, will now just keep counting down if server unable to be contacted.
- added a retry to the RCON command sending - set to 3, with a 10 sec delay.
This commit is contained in:
Brett Hewitson 2022-05-19 23:52:47 +10:00
parent f42da44940
commit 1d6907a471
22 changed files with 966 additions and 848 deletions

View file

@ -249,7 +249,7 @@
<value>https://servermanagers.freeforums.net/</value>
</setting>
<setting name="ServerStatusWatcher_LocalStatusQueryDelay" serializeAs="String">
<value>15000</value>
<value>5000</value>
</setting>
<setting name="ServerStatusWatcher_RemoteStatusQueryDelay" serializeAs="String">
<value>120000</value>

View file

@ -198,6 +198,7 @@
</ApplicationDefinition>
<Compile Include="Delegates\ServerStatusChangeDelegate.cs" />
<Compile Include="Enums\ServerSettingsResetAction.cs" />
<Compile Include="Enums\ServerUpdateType.cs" />
<Compile Include="Lib\Model\PlayerListParameters.cs" />
<Compile Include="Lib\Model\RconParameters.cs" />
<Compile Include="Lib\Serialization\IniFileEntryAttribute.cs" />

View file

@ -1833,7 +1833,7 @@ namespace ServerManagerTool {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("15000")]
[global::System.Configuration.DefaultSettingValueAttribute("5000")]
public int ServerStatusWatcher_LocalStatusQueryDelay {
get {
return ((int)(this["ServerStatusWatcher_LocalStatusQueryDelay"]));

View file

@ -510,7 +510,7 @@
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="ServerStatusWatcher_LocalStatusQueryDelay" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">15000</Value>
<Value Profile="(Default)">5000</Value>
</Setting>
<Setting Name="ServerStatusWatcher_RemoteStatusQueryDelay" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">120000</Value>

View file

@ -0,0 +1,13 @@
using System;
namespace ServerManagerTool.Enums
{
[Flags]
public enum ServerUpdateType
{
None = 0,
Server = 1,
Mods = 2,
ServerAndMods = Server | Mods,
}
}

View file

@ -400,8 +400,9 @@
<!--#region Server Monitor Window -->
<sys:String x:Key="ServerMonitor_Title">Server Monitor</sys:String>
<sys:String x:Key="ServerMonitor_SelectedColumnLabel">Selected</sys:String>
<sys:String x:Key="ServerMonitor_TotalCountLabel">Total Servers:</sys:String>
<sys:String x:Key="ServerMonitor_SelectedColumnLabel">Selected</sys:String>
<sys:String x:Key="ServerMonitor_ProfileColumnLabel">Profile</sys:String>
<sys:String x:Key="ServerMonitor_ServerColumnLabel">Server</sys:String>
<sys:String x:Key="ServerMonitor_MapColumnLabel">Map</sys:String>
<sys:String x:Key="ServerMonitor_ModsColumnLabel">Mods</sys:String>
@ -410,16 +411,21 @@
<sys:String x:Key="ServerMonitor_StatusColumnLabel">Status</sys:String>
<sys:String x:Key="ServerMonitor_CreateShortcutButtonTooltip">Create a desktop shortcut to open this form directly.</sys:String>
<sys:String x:Key="ServerMonitor_StartServersButtonTooltip">Start the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_ShutdownServersButtonLabel">Shutdown Selected Servers</sys:String>
<sys:String x:Key="ServerMonitor_ShutdownServersButtonTooltip">Shutdown the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_StopServersButtonLabel">Stop Selected Servers</sys:String>
<sys:String x:Key="ServerMonitor_StopServersButtonTooltip">Stop the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_RestartServersButtonTooltip">Restart the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_UpdateServersButtonTooltip">Update the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_BackupServersButtonTooltip">Backup the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_SelectAllServersButtonTooltip">Select all servers.</sys:String>
<sys:String x:Key="ServerMonitor_UnselectAllServersButtonTooltip">Unselect all servers.</sys:String>
<sys:String x:Key="ServerMonitor_StartServersButtonTooltip">Start the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_ShutdownServersButtonLabel">Shutdown Selected Servers</sys:String>
<sys:String x:Key="ServerMonitor_StopServersButtonLabel">Stop Selected Servers</sys:String>
<sys:String x:Key="ServerMonitor_RestartServersButtonTooltip">Restart the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_UpdateServersButtonLabel">Update/Verify Selected Servers</sys:String>
<sys:String x:Key="ServerMonitor_UpdateModsButtonLabel">Update Mods Only on Selected Servers</sys:String>
<sys:String x:Key="ServerMonitor_BackupServersButtonTooltip">Backup the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_CancelServersButtonTooltip">Cancel processing the selected servers.</sys:String>
<sys:String x:Key="ServerMonitor_ProcessServersSequentiallyLabel">Process Servers Sequentially</sys:String>
<sys:String x:Key="ServerMonitor_ProcessServersSequentiallyTooltip">If enabled, servers will be processed one after the other; otherwise they will all be processed at the same time.</sys:String>
<sys:String x:Key="ServerMonitor_SequentialProcessDelayLabel">Sequential Process Delay</sys:String>
<sys:String x:Key="ServerMonitor_SequentialProcessDelayTooltip">The number of seconds to wait before the next server will be processed.</sys:String>
<sys:String x:Key="ServerMonitor_PlayerListButtonTooltip">Open the Player List window.</sys:String>
<sys:String x:Key="ServerMonitor_RCONButtonTooltip">Open the RCON window.</sys:String>
<sys:String x:Key="ServerMonitor_StartServerTooltip">Start the server.</sys:String>
@ -427,6 +433,9 @@
<sys:String x:Key="ServerMonitor_InstallServerTooltip">Install the server.</sys:String>
<sys:String x:Key="ServerMonitor_UpgradeServerTooltip">Update/Verify the server.</sys:String>
<sys:String x:Key="ServerMonitor_ProcessServer_CancelledLabel">The process was cancelled by the user. You will need to the status of your servers.</sys:String>
<sys:String x:Key="ServerMonitor_ProcessServer_CancelRequestedLabel">A process cancellation request has been sent.</sys:String>
<sys:String x:Key="ServerMonitor_UpgradeServer_FailedTitle">Server Update Error</sys:String>
<sys:String x:Key="ServerMonitor_UpgradeServer_FailedLabel">Another server is being upgraded, wait until the upgrade has finished and try again.</sys:String>
<sys:String x:Key="ServerMonitor_CloseWindow_ConfirmTitle">Confirm Window Close</sys:String>
@ -1356,6 +1365,7 @@
<sys:String x:Key="DiscordBot_StartRequested">A start request for server '{0}' has been sent.</sys:String>
<sys:String x:Key="DiscordBot_StopRequested">A stop request for server '{0}' has been sent.</sys:String>
<sys:String x:Key="DiscordBot_UpdateRequested">An update request for server '{0}' has been sent.</sys:String>
<sys:String x:Key="DiscordBot_CommandComplete">The request for server '{0}' has completed.</sys:String>
<sys:String x:Key="DiscordBot_CountLabel">Count:</sys:String>
<sys:String x:Key="DiscordBot_MapLabel">Map:</sys:String>

View file

@ -37,9 +37,14 @@ namespace ServerManagerTool.Lib
public const int MUTEX_TIMEOUT = 5; // 5 minutes
public const int MUTEX_ATTEMPTDELAY = 5000; // 5 seconds
private const int STEAM_MAXRETRIES = 10;
private const int RCON_MAXRETRIES = 3;
private const int FILECOPY_MAXRETRIES = 3;
private const int MAXRETRIES_FILECOPY = 3;
private const int MAXRETRIES_RCON = 3;
private const int MAXRETRIES_STEAM = 10;
private const int DELAY_RCONCONNECTION = 1000; // 1 seconds
private const int DELAY_RCONRETRY = 10000; // 10 seconds
private const int DIRECTORIES_PER_LINE = 200;
public const int EXITCODE_NORMALEXIT = 0;
private const int EXITCODE_EXITWITHERRORS = 98;
@ -83,8 +88,6 @@ namespace ServerManagerTool.Lib
public const string LOGPREFIX_AUTOSHUTDOWN = "#AutoShutdownLogs";
public const string LOGPREFIX_AUTOUPDATE = "#AutoUpdateLogs";
private const int DIRECTORIES_PER_LINE = 200;
private static DateTime _startTime = DateTime.Now;
private static Dictionary<ServerProfileSnapshot, ServerProfile> _profiles = null;
@ -96,22 +99,22 @@ namespace ServerManagerTool.Lib
private QueryMaster.Rcon _rconConsole = null;
private bool _serverRunning = false;
public bool BackupWorldFile = Config.Default.BackupWorldFile;
public bool CheckForOnlinePlayers = Config.Default.ServerShutdown_CheckForOnlinePlayers;
public bool SendMessages = Config.Default.ServerShutdown_SendShutdownMessages;
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;
public string UpdateReason = null;
public ServerProcessType ServerProcess = ServerProcessType.Unknown;
public int ShutdownInterval = Config.Default.ServerShutdown_GracePeriod;
public ProgressDelegate ProgressCallback = null;
public ProcessWindowStyle SteamCMDProcessWindowStyle = ProcessWindowStyle.Minimized;
public ServerStatusChangeDelegate ServerStatusChangeCallback = null;
public bool BackupWorldFile { get; set; } = Config.Default.BackupWorldFile;
public bool CheckForOnlinePlayers { get; set; } = Config.Default.ServerShutdown_CheckForOnlinePlayers;
public bool DeleteOldBackupFiles { get; set; } = Config.Default.AutoBackup_DeleteOldFiles;
public int ExitCode { get; set; } = EXITCODE_NORMALEXIT;
public bool OutputLogs { get; set; } = false;
public bool PerformWorldSave { get; set; } = Config.Default.ServerShutdown_EnableWorldSave;
public bool SendAlerts { get; set; } = false;
public bool SendEmails { get; set; } = false;
public ProgressDelegate ProgressCallback { get; set; } = null;
public bool SendShutdownMessages { get; set; } = Config.Default.ServerShutdown_SendShutdownMessages;
public ServerProcessType ServerProcess { get; set; } = ServerProcessType.Unknown;
public ServerStatusChangeDelegate ServerStatusChangeCallback { get; set; } = null;
public int ShutdownInterval { get; set; } = Config.Default.ServerShutdown_GracePeriod;
public string ShutdownReason { get; set; } = null;
public ProcessWindowStyle SteamCMDProcessWindowStyle { get; set; } = ProcessWindowStyle.Minimized;
public string UpdateReason { get; set; } = null;
public ServerApp(bool resetStartTime = false)
{
@ -223,7 +226,7 @@ namespace ServerManagerTool.Lib
ExitCode = EXITCODE_NORMALEXIT;
}
private void ShutdownServer(bool restartServer, bool updateServer, bool steamCmdRemoveQuit, CancellationToken cancellationToken)
private void ShutdownServer(bool restartServer, ServerUpdateType updateType, bool steamCmdRemoveQuit, CancellationToken cancellationToken)
{
if (_profile == null)
{
@ -277,13 +280,13 @@ namespace ServerManagerTool.Lib
return;
}
if (updateServer)
if (updateType != ServerUpdateType.None)
{
try
{
LogProfileMessage("");
ServerStatusChangeCallback?.Invoke(ServerStatus.Updating);
UpgradeLocal(true, true, steamCmdRemoveQuit, cancellationToken);
UpgradeLocal(updateType, true, steamCmdRemoveQuit, cancellationToken);
}
finally
{
@ -419,7 +422,6 @@ namespace ServerManagerTool.Lib
QueryMaster.Server gameServer = null;
bool sent = false;
var serverAccessible = true;
try
{
@ -481,43 +483,17 @@ namespace ServerManagerTool.Lib
}
catch (Exception ex)
{
serverAccessible = false;
LogProfileError("Error getting online players.", false);
LogProfileError(ex.Message, false);
LogProfileError("", false);
}
if (!serverAccessible)
{
LogProfileMessage("Server not accessible, shutdown timer cancelled.");
break;
}
// check if anyone is logged into the server
if (playerCount == 0)
{
LogProfileMessage("No online players, shutdown timer cancelled.");
break;
}
if (playerCount < 0)
{
LogProfileMessage("Unknown online players, shutdown timer cancelled.");
break;
}
}
else
{
try
{
var serverInfo = gameServer?.GetInfo();
serverAccessible = true;
}
catch
{
serverAccessible = false;
}
}
var message = string.Empty;
@ -583,7 +559,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 && PerformWorldSave)
//if (PerformWorldSave)
//{
// try
// {
@ -731,7 +707,7 @@ namespace ServerManagerTool.Lib
ExitCode = EXITCODE_SHUTDOWN_TIMEOUT;
}
private void UpgradeLocal(bool validate, bool updateMods, bool steamCmdRemoveQuit, CancellationToken cancellationToken)
private void UpgradeLocal(ServerUpdateType updateType, bool validate, bool steamCmdRemoveQuit, CancellationToken cancellationToken)
{
if (_profile == null)
{
@ -756,67 +732,93 @@ namespace ServerManagerTool.Lib
var downloadSuccessful = false;
var success = false;
// *********************
// Server Update Section
// *********************
LogProfileMessage("\r\n");
LogProfileMessage("Starting server update.");
LogProfileMessage("Updating server from steam.\r\n");
downloadSuccessful = !Config.Default.SteamCmdRedirectOutput;
void serverOutputHandler(object s, DataReceivedEventArgs e)
{
var dataValue = e.Data ?? string.Empty;
LogProfileMessage(dataValue);
if (!gotNewVersion && dataValue.Contains("downloading,"))
{
gotNewVersion = true;
}
if (dataValue.StartsWith("Success!"))
{
downloadSuccessful = true;
}
}
var steamCmdArgs = SteamUtils.BuildSteamCmdArguments(steamCmdRemoveQuit, Config.Default.SteamCmdInstallServerArgsFormat, Config.Default.SteamCmd_AnonymousUsername, _profile.InstallDirectory, string.Empty, Config.Default.AppIdServer, string.Empty, validate ? "validate" : string.Empty);
var steamCmdArgs = string.Empty;
var workingDirectory = Config.Default.DataPath;
if (steamCmdRemoveQuit)
SteamCMDProcessWindowStyle = ProcessWindowStyle.Normal;
success = ServerUpdater.UpgradeServerAsync(steamCmdFile, steamCmdArgs, workingDirectory, null, null, _profile.InstallDirectory, Config.Default.SteamCmdRedirectOutput ? (DataReceivedEventHandler)serverOutputHandler : null, cancellationToken, SteamCMDProcessWindowStyle).Result;
if (success && downloadSuccessful)
if ((updateType & ServerUpdateType.Server) == ServerUpdateType.Server)
{
LogProfileMessage("Finished server update.");
if (Directory.Exists(_profile.InstallDirectory))
{
if (!Config.Default.SteamCmdRedirectOutput)
// check if any of the server files have changed.
gotNewVersion = HasNewServerVersion(_profile.InstallDirectory, startTime);
LogProfileMessage($"New server version - {gotNewVersion.ToString().ToUpperInvariant()}.");
}
// *********************
// Server Update Section
// *********************
LogProfileMessage("\r\n");
LogProfileMessage("Starting server update.");
LogProfileMessage("Updating server from steam.\r\n");
downloadSuccessful = !Config.Default.SteamCmdRedirectOutput;
void serverOutputHandler(object s, DataReceivedEventArgs e)
{
var dataValue = e.Data ?? string.Empty;
LogProfileMessage(dataValue);
if (!gotNewVersion && dataValue.Contains("downloading,"))
{
gotNewVersion = true;
}
if (dataValue.StartsWith("Success!"))
{
downloadSuccessful = true;
}
}
// create the branch arguments
var steamCmdInstallServerBetaArgs = new StringBuilder();
if (!string.IsNullOrWhiteSpace(_profile.BranchName))
{
steamCmdInstallServerBetaArgs.AppendFormat(Config.Default.SteamCmdInstallServerBetaNameArgsFormat, _profile.BranchName);
if (!string.IsNullOrWhiteSpace(_profile.BranchPassword))
{
steamCmdInstallServerBetaArgs.Append(" ");
steamCmdInstallServerBetaArgs.AppendFormat(Config.Default.SteamCmdInstallServerBetaPasswordArgsFormat, _profile.BranchPassword);
}
}
steamCmdArgs = SteamUtils.BuildSteamCmdArguments(steamCmdRemoveQuit, Config.Default.SteamCmdInstallServerArgsFormat, Config.Default.SteamCmd_AnonymousUsername, _profile.InstallDirectory, Config.Default.AppIdServer, steamCmdInstallServerBetaArgs.ToString(), validate ? "validate" : string.Empty);
if (steamCmdRemoveQuit)
{
SteamCMDProcessWindowStyle = ProcessWindowStyle.Normal;
}
success = ServerUpdater.UpgradeServerAsync(steamCmdFile, steamCmdArgs, workingDirectory, null, null, _profile.InstallDirectory, Config.Default.SteamCmdRedirectOutput ? (DataReceivedEventHandler)serverOutputHandler : null, cancellationToken, SteamCMDProcessWindowStyle).Result;
if (success && downloadSuccessful)
{
LogProfileMessage("Finished server update.");
if (Directory.Exists(_profile.InstallDirectory))
{
if (!Config.Default.SteamCmdRedirectOutput)
{
// check if any of the server files have changed.
gotNewVersion = HasNewServerVersion(_profile.InstallDirectory, startTime);
}
LogProfileMessage($"New server version - {gotNewVersion.ToString().ToUpperInvariant()}.");
}
LogProfileMessage("\r\n");
}
else
{
success = false;
LogProfileMessage("****************************");
LogProfileMessage("ERROR: Failed server update.");
LogProfileMessage("****************************");
LogProfileMessage("Check steamcmd logs for more information why the server update failed.\r\n");
if (Config.Default.SteamCmdRedirectOutput)
{
LogProfileMessage($"If the server update keeps failing try disabling the '{_globalizer.GetResourceString("GlobalSettings_SteamCmdRedirectOutputLabel")}' option in the settings window.\r\n");
}
ExitCode = EXITCODE_SERVERUPDATEFAILED;
}
}
else
{
success = false;
LogProfileMessage("****************************");
LogProfileMessage("ERROR: Failed server update.");
LogProfileMessage("****************************");
LogProfileMessage("Check steamcmd logs for more information why the server update failed.\r\n");
if (Config.Default.SteamCmdRedirectOutput)
LogProfileMessage($"If the server update keeps failing try disabling the '{_globalizer.GetResourceString("GlobalSettings_SteamCmdRedirectOutputLabel")}' option in the settings window.\r\n");
ExitCode = EXITCODE_SERVERUPDATEFAILED;
success = true;
}
// check if we need to update the mods
if (updateMods)
if ((updateType & ServerUpdateType.Mods) == ServerUpdateType.Mods)
{
if (success)
{
@ -861,7 +863,9 @@ namespace ServerManagerTool.Lib
modTitle = $"{modId} - {modDetail?.title ?? "<unknown>"}";
if (modDetail != null)
{
LogProfileMessage($"{modDetail.title}.\r\n");
}
var modCachePath = ModUtils.GetModCachePath(modId);
var cacheTimeFile = ModUtils.GetLatestModCacheTimeFile(modId);
@ -944,9 +948,13 @@ namespace ServerManagerTool.Lib
steamCmdArgs = string.Empty;
if (Config.Default.SteamCmd_UseAnonymousCredentials)
{
steamCmdArgs = SteamUtils.BuildSteamCmdArguments(steamCmdRemoveQuit, Config.Default.SteamCmdInstallModArgsFormat, Config.Default.SteamCmd_AnonymousUsername, Config.Default.AppId, modId);
}
else
{
steamCmdArgs = SteamUtils.BuildSteamCmdArguments(steamCmdRemoveQuit, Config.Default.SteamCmdInstallModArgsFormat, Config.Default.SteamCmd_Username, Config.Default.AppId, modId);
}
modSuccess = ServerUpdater.UpgradeModsAsync(steamCmdFile, steamCmdArgs, workingDirectory, null, null, Config.Default.SteamCmdRedirectOutput ? modOutputHandler : null, cancellationToken, SteamCMDProcessWindowStyle).Result;
if (modSuccess && downloadSuccessful)
@ -990,10 +998,14 @@ namespace ServerManagerTool.Lib
}
}
else
{
modSuccess = !updateError;
}
}
else
{
modSuccess = !updateError;
}
if (copyMod)
{
@ -1258,7 +1270,7 @@ namespace ServerManagerTool.Lib
{
// perform a steamcmd validate to confirm all the files
LogProfileMessage("Validating server files (*new*).");
UpgradeLocal(true, false, false, CancellationToken.None);
UpgradeLocal(ServerUpdateType.Server, true, false, CancellationToken.None);
LogProfileMessage("Validated server files (*new*).");
}
@ -1637,7 +1649,7 @@ namespace ServerManagerTool.Lib
LogError(logError);
// check if we have reached the max failed attempt limit.
if (!Config.Default.AutoUpdate_RetryOnFail || attempt >= STEAM_MAXRETRIES)
if (!Config.Default.AutoUpdate_RetryOnFail || attempt >= MAXRETRIES_STEAM)
{
// failed max limit reached
if (Config.Default.SteamCmdRedirectOutput)
@ -1759,7 +1771,7 @@ namespace ServerManagerTool.Lib
LogBranchError(branchName, logError);
// check if we have reached the max failed attempt limit.
if (!Config.Default.AutoUpdate_RetryOnFail || attempt >= STEAM_MAXRETRIES)
if (!Config.Default.AutoUpdate_RetryOnFail || attempt >= MAXRETRIES_STEAM)
{
// failed max limit reached
if (Config.Default.SteamCmdRedirectOutput)
@ -2173,7 +2185,7 @@ namespace ServerManagerTool.Lib
catch (IOException)
{
retries++;
if (retries >= FILECOPY_MAXRETRIES) throw;
if (retries >= MAXRETRIES_FILECOPY) throw;
Task.Delay(5000).Wait();
}
}
@ -2579,8 +2591,7 @@ namespace ServerManagerTool.Lib
if (string.IsNullOrWhiteSpace(command))
return false;
var rconRetries = RCON_MAXRETRIES;
var sent = false;
var rconRetries = MAXRETRIES_RCON;
try
{
@ -2596,23 +2607,23 @@ namespace ServerManagerTool.Lib
if (_rconConsole == null)
{
LogProfileError("RCON connection could not be created.", false);
rconRetries--;
}
else
{
try
{
_rconConsole.SendCommand(command);
sent = true;
return true;
}
catch (Exception ex)
{
LogProfileError(ex.Message, false);
LogProfileError(ex.StackTrace, false);
}
break;
}
rconRetries--;
Task.Delay(DELAY_RCONRETRY).Wait();
}
}
finally
@ -2620,7 +2631,7 @@ namespace ServerManagerTool.Lib
CloseRconConsole();
}
return sent;
return false;
}
private bool SendMessage(string message, CancellationToken token)
@ -2630,7 +2641,7 @@ namespace ServerManagerTool.Lib
private bool SendMessage(string mode, string message, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(message) || !SendMessages)
if (string.IsNullOrWhiteSpace(message) || !SendShutdownMessages)
return false;
var sent = SendCommand($"{GetRconMessageCommand(mode)} {message}", token);
@ -2696,7 +2707,7 @@ namespace ServerManagerTool.Lib
_rconConsole.Dispose();
_rconConsole = null;
Task.Delay(1000).Wait();
Task.Delay(DELAY_RCONCONNECTION).Wait();
}
}
@ -2722,7 +2733,7 @@ namespace ServerManagerTool.Lib
LogProfileDebug($"SUCCESS: {nameof(SetupRconConsole)} - ServerQuery was created.", false);
Task.Delay(1000).Wait();
Task.Delay(DELAY_RCONCONNECTION).Wait();
LogProfileMessage($"Opening RCON connection to server ({_profile.ServerIPAddress}:{_profile.RconPort}).", false);
@ -2822,7 +2833,7 @@ namespace ServerManagerTool.Lib
return ExitCode;
}
public int PerformProfileShutdown(ServerProfileSnapshot profile, bool performRestart, bool performUpdate, bool checkGracePeriod, bool steamCmdRemoveQuit, CancellationToken cancellationToken)
public int PerformProfileShutdown(ServerProfileSnapshot profile, bool performRestart, ServerUpdateType updateType, bool checkGracePeriod, bool steamCmdRemoveQuit, CancellationToken cancellationToken)
{
_profile = profile;
@ -2864,7 +2875,7 @@ namespace ServerManagerTool.Lib
{
LogProfileMessage("Server lock established.\r\n");
ShutdownServer(performRestart, performUpdate, steamCmdRemoveQuit, cancellationToken);
ShutdownServer(performRestart, updateType, steamCmdRemoveQuit, cancellationToken);
if (ExitCode != EXITCODE_NORMALEXIT)
{
@ -3274,7 +3285,7 @@ namespace ServerManagerTool.Lib
ServerProcess = type,
SteamCMDProcessWindowStyle = ProcessWindowStyle.Hidden
};
exitCode = app.PerformProfileShutdown(profile, performRestart, performUpdate, true, false, CancellationToken.None);
exitCode = app.PerformProfileShutdown(profile, performRestart, performUpdate ? ServerUpdateType.ServerAndMods : ServerUpdateType.None, true, false, CancellationToken.None);
if (profile.ServerUpdated)
{

View file

@ -54,19 +54,19 @@ namespace ServerManagerTool.Utils
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) };
case CommandType.Restart:
if (Config.Default.AllowDiscordRestart)
return RestartServer(channelId, profileIdOrAlias, token);
return StartServer(channelId, profileIdOrAlias, restart: true, token);
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) };
case CommandType.Shutdown:
if (Config.Default.AllowDiscordShutdown)
return StopServer(channelId, profileIdOrAlias, true, token);
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) };
case CommandType.Stop:
if (Config.Default.AllowDiscordStop)
return StopServer(channelId, profileIdOrAlias, false, token);
return StopServer(channelId, profileIdOrAlias, shutdown: true, token);
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) };
case CommandType.Start:
if (Config.Default.AllowDiscordStart)
return StartServer(channelId, profileIdOrAlias, token);
return StartServer(channelId, profileIdOrAlias, restart: false, token);
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) };
case CommandType.Stop:
if (Config.Default.AllowDiscordStop)
return StopServer(channelId, profileIdOrAlias, shutdown: false, token);
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_CommandNotEnabled"), commandType) };
case CommandType.Update:
if (Config.Default.AllowDiscordUpdate)
@ -336,7 +336,6 @@ namespace ServerManagerTool.Utils
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
@ -366,11 +365,11 @@ namespace ServerManagerTool.Utils
return responseList;
}
private static IList<string> RestartServer(string channelId, string profileIdOrAlias, CancellationToken token)
private static IList<string> StartServer(string channelId, string profileIdOrAlias, bool restart, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(profileIdOrAlias))
{
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), CommandType.Restart) };
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), restart ? CommandType.Restart : CommandType.Start) };
}
var profileList = new List<ServerProfileSnapshot>();
@ -402,11 +401,16 @@ namespace ServerManagerTool.Utils
{
foreach (var server in serverList)
{
if (!server.Profile.AllowDiscordRestart)
if (restart && !server.Profile.AllowDiscordRestart)
{
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Restart, server.Profile.ProfileName));
continue;
}
if (!restart && !server.Profile.AllowDiscordStart)
{
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Start, server.Profile.ProfileName));
continue;
}
// check if another command is being run against the profile
if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID))
@ -424,12 +428,20 @@ namespace ServerManagerTool.Utils
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
continue;
case ServerStatus.Running:
if (!restart)
{
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
continue;
}
break;
case ServerStatus.Updating:
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Restart);
_currentProfileCommands.Add(server.Profile.ProfileID, restart ? CommandType.Restart : CommandType.Start);
var profile = ServerProfileSnapshot.Create(server.Profile);
profile.AutoRestartIfShutdown = true;
profileList.Add(profile);
@ -441,7 +453,6 @@ namespace ServerManagerTool.Utils
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
@ -461,11 +472,14 @@ namespace ServerManagerTool.Utils
Task.Run(() =>
{
app.PerformProfileShutdown(profile, true, false, false, false, token);
app.PerformProfileShutdown(profile, true, ServerUpdateType.None, false, false, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName));
if (restart)
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName));
else
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName));
}
return responseList;
@ -475,7 +489,7 @@ namespace ServerManagerTool.Utils
{
if (string.IsNullOrWhiteSpace(profileIdOrAlias))
{
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), CommandType.Stop) };
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), shutdown ? CommandType.Shutdown : CommandType.Stop) };
}
var profileList = new List<ServerProfileSnapshot>();
@ -507,7 +521,12 @@ namespace ServerManagerTool.Utils
{
foreach (var server in serverList)
{
if (!server.Profile.AllowDiscordStop)
if (shutdown && !server.Profile.AllowDiscordShutdown)
{
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Shutdown, server.Profile.ProfileName));
continue;
}
if (!shutdown && !server.Profile.AllowDiscordStop)
{
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Stop, server.Profile.ProfileName));
continue;
@ -535,7 +554,7 @@ namespace ServerManagerTool.Utils
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Stop);
_currentProfileCommands.Add(server.Profile.ProfileID, shutdown ? CommandType.Shutdown : CommandType.Stop);
profileList.Add(ServerProfileSnapshot.Create(server.Profile));
}
}
@ -546,7 +565,6 @@ namespace ServerManagerTool.Utils
var app = new ServerApp(true)
{
BackupWorldFile = shutdown,
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
PerformWorldSave = shutdown,
SendAlerts = true,
@ -570,7 +588,7 @@ namespace ServerManagerTool.Utils
Task.Run(() =>
{
app.PerformProfileShutdown(profile, false, false, false, false, token);
app.PerformProfileShutdown(profile, false, ServerUpdateType.None, false, false, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
@ -583,112 +601,6 @@ namespace ServerManagerTool.Utils
return responseList;
}
private static IList<string> StartServer(string channelId, string profileIdOrAlias, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(profileIdOrAlias))
{
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), CommandType.Start) };
}
var profileList = new List<ServerProfileSnapshot>();
var responseList = new List<string>();
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.AllowDiscordStart)
{
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_CommandDisabledProfile"), CommandType.Start, 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.Running:
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.Start);
var profile = ServerProfileSnapshot.Create(server.Profile);
profile.AutoRestartIfShutdown = true;
profileList.Add(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.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);
}
};
Task.Run(() =>
{
app.PerformProfileShutdown(profile, true, false, false, false, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
responseList.Add(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName));
}
return responseList;
}
private static IList<string> UpdateServer(string channelId, string profileIdOrAlias, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(profileIdOrAlias))
@ -769,7 +681,6 @@ namespace ServerManagerTool.Utils
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
@ -789,7 +700,7 @@ namespace ServerManagerTool.Utils
Task.Run(() =>
{
app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, true, false, false, token);
app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, ServerUpdateType.ServerAndMods, false, false, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);

View file

@ -6,6 +6,7 @@
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:sm="clr-namespace:ServerManagerTool"
xmlns:cctl="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common"
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"
@ -31,6 +32,12 @@
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/DropArrow.ico,Size=32}" Width="8" Margin="2,0,0,0" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="UpdateButtonContent">
<StackPanel Orientation="Horizontal">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}" Width="16" VerticalAlignment="Center"/>
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/DropArrow.ico,Size=32}" Width="8" Margin="2,0,0,0" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
@ -38,6 +45,7 @@
<DockPanel x:Name="dockPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" MinHeight="200"/>
<RowDefinition Height="Auto"/>
@ -52,6 +60,8 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
@ -82,34 +92,39 @@
<controls:DropDownButton.Menu>
<ContextMenu>
<MenuItem Header="{DynamicResource ServerMonitor_ShutdownServersButtonLabel}" Command="{Binding ShutdownServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_ShutdownServersButtonTooltip}">
<MenuItem.Icon>
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource ServerMonitor_StopServersButtonLabel}" Command="{Binding StopServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_StopServersButtonTooltip}">
<MenuItem.Icon>
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource ServerMonitor_ShutdownServersButtonLabel}" Command="{Binding ShutdownServersCommand}" CommandParameter="{Binding}"/>
<MenuItem Header="{DynamicResource ServerMonitor_StopServersButtonLabel}" Command="{Binding StopServersCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
</controls:DropDownButton.Menu>
</controls:DropDownButton>
<Button Grid.Row="0" Grid.Column="6" Width="22" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" Command="{Binding UpdateServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_UpdateServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
</Button>
<controls:DropDownButton Grid.Row="0" Grid.Column="6" Width="35" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Center">
<ContentControl ContentTemplate="{StaticResource UpdateButtonContent}" />
<controls:DropDownButton.Menu>
<ContextMenu>
<MenuItem Header="{DynamicResource ServerMonitor_UpdateServersButtonLabel}" Command="{Binding UpdateServersCommand}" CommandParameter="{Binding}"/>
<MenuItem Header="{DynamicResource ServerMonitor_UpdateModsButtonLabel}" Command="{Binding UpdateModsCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
</controls:DropDownButton.Menu>
</controls:DropDownButton>
<Button Grid.Row="0" Grid.Column="7" Width="22" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding BackupServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_BackupServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Save.ico,Size=32}"/>
</Button>
<StackPanel Grid.Row="0" Grid.Column="8" Orientation="Horizontal">
<Button Grid.Row="0" Grid.Column="8" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding CancelServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_CancelServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</Button>
<CheckBox Grid.Row="0" Grid.Column="9" Content="{DynamicResource ServerMonitor_ProcessServersSequentiallyLabel}" IsChecked="{Binding ProcessServersSequentially}" Margin="10,5,5,0" VerticalAlignment="Center" ToolTip="{DynamicResource ServerMonitor_ProcessServersSequentiallyTooltip}"/>
<StackPanel Grid.Row="0" Grid.Column="10" Orientation="Horizontal">
<TextBlock Margin="30,5,5,0" Text="{DynamicResource ServerMonitor_TotalCountLabel}" VerticalAlignment="Center" />
<TextBlock Margin="5,5,5,0" Text="{Binding ServerManager.Servers.Count}" VerticalAlignment="Center" />
</StackPanel>
<Button Grid.Row="0" Grid.Column="9" Height="22" Margin="5,5,5,0" Background="#00AA00" Foreground="White" Padding="1" BorderThickness="1" BorderBrush="White" ContentStringFormat="{DynamicResource MainWindow_UpdateToLabelFormat}" Content="{Binding LatestServerManagerVersion}" Click="UpgradeApplication_Click" VerticalAlignment="Center" >
<Button Grid.Row="0" Grid.Column="11" Height="22" Margin="5,5,5,0" Background="#00AA00" Foreground="White" Padding="1" BorderThickness="1" BorderBrush="White" ContentStringFormat="{DynamicResource MainWindow_UpdateToLabelFormat}" Content="{Binding LatestServerManagerVersion}" Click="UpgradeApplication_Click" VerticalAlignment="Center" >
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
@ -124,7 +139,7 @@
</Button.Style>
</Button>
<Button Grid.Row="0" Grid.Column="10" Width="22" Height="22" Margin="5,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="PatchNotes_Click" ToolTip="{DynamicResource ServerSettings_PatchNotesTooltip}">
<Button Grid.Row="0" Grid.Column="12" Width="22" Height="22" Margin="5,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="PatchNotes_Click" ToolTip="{DynamicResource ServerSettings_PatchNotesTooltip}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
@ -141,7 +156,9 @@
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/ChangeNotes.ico,Size=32}"/>
</Button>
<DataGrid Name="ServersGrid" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="11" Margin="5,5,5,0" HorizontalAlignment="Stretch" ItemsSource="{Binding ServerManager.Servers}" GridLinesVisibility="Horizontal" HeadersVisibility="All" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserSortColumns="False" CanUserResizeRows="False" RowHeaderWidth="25" SelectionMode="Single" PreviewMouseLeftButtonDown="OnMouseLeftButtonDown">
<cctl:AnnotatedSlider Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="13" Margin="1" Label="{DynamicResource ServerMonitor_SequentialProcessDelayLabel}" Value="{Binding SequentialProcessDelay}" Minimum="0" Maximum="300" SmallChange="1" LargeChange="5" TickFrequency="1" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Seconds}" Visibility="{Binding ProcessServersSequentially, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTip="{DynamicResource ServerMonitor_SequentialProcessDelayTooltip}"/>
<DataGrid Name="ServersGrid" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="13" Margin="5,5,5,0" HorizontalAlignment="Stretch" ItemsSource="{Binding ServerManager.Servers}" GridLinesVisibility="Horizontal" HeadersVisibility="All" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserSortColumns="False" CanUserResizeRows="False" RowHeaderWidth="25" SelectionMode="Single" PreviewMouseLeftButtonDown="OnMouseLeftButtonDown">
<DataGrid.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type DataGridCell}">
@ -211,9 +228,9 @@
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn Width="Auto" MinWidth="100">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_ServerColumnLabel}" />
<TextBlock Text="{DynamicResource ServerMonitor_ProfileColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
@ -228,7 +245,24 @@
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="Auto" MinWidth="150">
<DataGridTemplateColumn Width="*" MinWidth="100">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_ServerColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Profile.ServerName}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="Auto" MinWidth="100">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_MapColumnLabel}" />
</DataGridTemplateColumn.Header>
@ -602,9 +636,9 @@
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="11" Height="5" ShowsPreview="True" HorizontalAlignment="Stretch" VerticalAlignment="Center" Opacity="0"/>
<GridSplitter Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="13" Height="5" ShowsPreview="True" HorizontalAlignment="Stretch" VerticalAlignment="Center" Opacity="0"/>
<RichTextBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="11" Margin="5,0,5,5" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto" IsReadOnlyCaretVisible="True" IsReadOnly="True" IsTabStop="False">
<RichTextBox Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="13" Margin="5,0,5,5" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto" IsReadOnlyCaretVisible="True" IsReadOnly="True" IsTabStop="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged" >
<sm:ScrollToBottomAction IsEnabled="True"/>

View file

@ -29,6 +29,17 @@ namespace ServerManagerTool.Windows
/// </summary>
public partial class ServerMonitorWindow : Window
{
public class ServerMonitorOutput_Critical : Run
{
public ServerMonitorOutput_Critical(string value)
: base(value)
{
Foreground = Brushes.Red;
FontWeight = FontWeights.Bold;
FontSize = FontSize + 2;
}
}
public class ServerMonitorOutput_Error : Run
{
public ServerMonitorOutput_Error(string value)
@ -47,6 +58,16 @@ namespace ServerManagerTool.Windows
}
}
public class ServerMonitorOutput_Warning : Run
{
public ServerMonitorOutput_Warning(string value)
: base(value)
{
Foreground = Brushes.Orange;
FontWeight = FontWeights.Bold;
}
}
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly List<ServerMonitorWindow> Windows = new List<ServerMonitorWindow>();
@ -63,6 +84,8 @@ namespace ServerManagerTool.Windows
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 static readonly DependencyProperty ProcessServersSequentiallyProperty = DependencyProperty.Register(nameof(ProcessServersSequentially), typeof(bool), typeof(ServerMonitorWindow), new PropertyMetadata(false));
public static readonly DependencyProperty SequentialProcessDelayProperty = DependencyProperty.Register(nameof(SequentialProcessDelay), typeof(int), typeof(ServerMonitorWindow), new PropertyMetadata(10));
public ServerMonitorWindow(): this(null)
{
@ -121,6 +144,18 @@ namespace ServerManagerTool.Windows
set { SetValue(CancellationTokenSourceProperty, value); }
}
public bool ProcessServersSequentially
{
get { return (bool)GetValue(ProcessServersSequentiallyProperty); }
set { SetValue(ProcessServersSequentiallyProperty, value); }
}
public int SequentialProcessDelay
{
get { return (int)GetValue(SequentialProcessDelayProperty); }
set { SetValue(SequentialProcessDelayProperty, value); }
}
private void ServerMonitorWindow_Loaded(object sender, RoutedEventArgs e)
{
if (ServerManager == null)
@ -454,22 +489,52 @@ namespace ServerManagerTool.Windows
}
}
public void AddCriticalBlockContent(string message)
{
TaskUtils.RunOnUIThreadAsync(() =>
{
var p = new Paragraph();
p.Inlines.Add(new ServerMonitorOutput_Critical(message));
ConsoleContent.Blocks.Add(p);
}).DoNotWait();
}
public void AddErrorBlockContent(string message)
{
var p = new Paragraph();
TaskUtils.RunOnUIThreadAsync(() =>
{
var p = new Paragraph();
p.Inlines.Add(new ServerMonitorOutput_Error(message));
p.Inlines.Add(new ServerMonitorOutput_Error(message));
ConsoleContent.Blocks.Add(p);
ConsoleContent.Blocks.Add(p);
}).DoNotWait();
}
public void AddMessageBlockContent(string message)
{
var p = new Paragraph();
TaskUtils.RunOnUIThreadAsync(() =>
{
var p = new Paragraph();
p.Inlines.Add(new ServerMonitorOutput_Success(message));
p.Inlines.Add(new ServerMonitorOutput_Success(message));
ConsoleContent.Blocks.Add(p);
ConsoleContent.Blocks.Add(p);
}).DoNotWait();
}
public void AddWarningBlockContent(string message)
{
TaskUtils.RunOnUIThreadAsync(() =>
{
var p = new Paragraph();
p.Inlines.Add(new ServerMonitorOutput_Warning(message));
ConsoleContent.Blocks.Add(p);
}).DoNotWait();
}
public void ClearBlockContents()
@ -782,7 +847,10 @@ namespace ServerManagerTool.Windows
return new RelayCommand<object>(
execute: async (_) =>
{
await BackupSelectedServersAsync();
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await BackupSelectedServersAsync(processServersSequentially, sequentialProcessDelay);
},
canExecute: (_) =>
{
@ -793,6 +861,25 @@ namespace ServerManagerTool.Windows
}
}
public ICommand CancelServersCommand
{
get
{
return new RelayCommand<object>(
execute: (_) =>
{
CancellationTokenSource.Cancel();
AddWarningBlockContent(_globalizer.GetResourceString("ServerMonitor_ProcessServer_CancelRequestedLabel"));
},
canExecute: (_) =>
{
return CancellationTokenSource != null && !CancellationTokenSource.IsCancellationRequested;
}
);
}
}
public ICommand RestartServersCommand
{
get
@ -800,7 +887,10 @@ namespace ServerManagerTool.Windows
return new RelayCommand<object>(
execute: async (_) =>
{
await RestartSelectedServersAsync();
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await StartSelectedServersAsync(restart: true, processServersSequentially, sequentialProcessDelay);
},
canExecute: (_) =>
{
@ -818,7 +908,10 @@ namespace ServerManagerTool.Windows
return new RelayCommand<object>(
execute: async (_) =>
{
await StopSelectedServersAsync(true);
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await StopSelectedServersAsync(shutdown: true, processServersSequentially, sequentialProcessDelay);
},
canExecute: (_) =>
{
@ -836,7 +929,10 @@ namespace ServerManagerTool.Windows
return new RelayCommand<object>(
execute: async (_) =>
{
await StartSelectedServersAsync();
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await StartSelectedServersAsync(restart: false, processServersSequentially, sequentialProcessDelay);
},
canExecute: (_) =>
{
@ -854,7 +950,31 @@ namespace ServerManagerTool.Windows
return new RelayCommand<object>(
execute: async (_) =>
{
await StopSelectedServersAsync(false);
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await StopSelectedServersAsync(shutdown: false, processServersSequentially, sequentialProcessDelay);
},
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 UpdateModsCommand
{
get
{
return new RelayCommand<object>(
execute: async (_) =>
{
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await UpdateSelectedServersAsync(updateModsOnly: true, processServersSequentially, sequentialProcessDelay);
},
canExecute: (_) =>
{
@ -872,7 +992,10 @@ namespace ServerManagerTool.Windows
return new RelayCommand<object>(
execute: async (_) =>
{
await UpdateSelectedServersAsync();
var processServersSequentially = ProcessServersSequentially;
var sequentialProcessDelay = SequentialProcessDelay;
await UpdateSelectedServersAsync(updateModsOnly: false, processServersSequentially, sequentialProcessDelay);
},
canExecute: (_) =>
{
@ -1014,7 +1137,7 @@ namespace ServerManagerTool.Windows
}
}
private async Task BackupSelectedServersAsync()
private async Task BackupSelectedServersAsync(bool processServersSequentially, int sequentialProcessDelay)
{
if (CancellationTokenSource != null)
return;
@ -1039,7 +1162,7 @@ namespace ServerManagerTool.Windows
// 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));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ServerName));
continue;
}
@ -1050,7 +1173,7 @@ namespace ServerManagerTool.Windows
case ServerStatus.Uninstalled:
case ServerStatus.Unknown:
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString));
continue;
}
@ -1066,7 +1189,6 @@ namespace ServerManagerTool.Windows
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
@ -1087,6 +1209,10 @@ namespace ServerManagerTool.Windows
var task = Task.Run(() =>
{
app.PerformProfileBackup(profile, token);
Task.Delay(5000).Wait();
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName));
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
@ -1099,113 +1225,22 @@ namespace ServerManagerTool.Windows
{
await Task.WhenAll(tasks);
}
catch { }
catch (OperationCanceledException)
{
AddCriticalBlockContent(_globalizer.GetResourceString("ServerMonitor_ProcessServer_CancelledLabel"));
}
catch (Exception)
{
}
finally
{
_currentProfileCommands.Clear();
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<ServerProfileSnapshot>();
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<Task>();
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()
private async Task StartSelectedServersAsync(bool restart, bool processServersSequentially, int sequentialProcessDelay)
{
if (CancellationTokenSource != null)
return;
@ -1230,7 +1265,7 @@ namespace ServerManagerTool.Windows
// 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));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ServerName));
continue;
}
@ -1238,18 +1273,25 @@ namespace ServerManagerTool.Windows
{
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));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString));
continue;
case ServerStatus.Running:
if (!restart)
{
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString));
continue;
}
break;
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName));
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Start);
_currentProfileCommands.Add(server.Profile.ProfileID, restart ? CommandType.Restart : CommandType.Start);
var profile = ServerProfileSnapshot.Create(server.Profile);
profile.AutoRestartIfShutdown = true;
profileList.Add(profile);
@ -1263,7 +1305,6 @@ namespace ServerManagerTool.Windows
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
@ -1283,28 +1324,42 @@ namespace ServerManagerTool.Windows
var task = Task.Run(() =>
{
app.PerformProfileShutdown(profile, true, false, false, false, token);
app.PerformProfileShutdown(profile, true, ServerUpdateType.None, false, false, token);
Task.Delay(5000).Wait();
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName));
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
tasks.Add(task);
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName));
if (restart)
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_RestartRequested"), profile.ServerName));
else
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StartRequested"), profile.ServerName));
}
try
{
await Task.WhenAll(tasks);
}
catch { }
catch (OperationCanceledException)
{
AddCriticalBlockContent(_globalizer.GetResourceString("ServerMonitor_ProcessServer_CancelledLabel"));
}
catch (Exception)
{
}
finally
{
_currentProfileCommands.Clear();
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}
}
private async Task StopSelectedServersAsync(bool shutdown)
private async Task StopSelectedServersAsync(bool shutdown, bool processServersSequentially, int sequentialProcessDelay)
{
if (CancellationTokenSource != null)
return;
@ -1331,7 +1386,7 @@ namespace ServerManagerTool.Windows
// 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));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ServerName));
continue;
}
@ -1342,11 +1397,11 @@ namespace ServerManagerTool.Windows
case ServerStatus.Stopped:
case ServerStatus.Uninstalled:
case ServerStatus.Unknown:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString));
continue;
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName));
continue;
}
@ -1363,7 +1418,6 @@ namespace ServerManagerTool.Windows
var app = new ServerApp(true)
{
BackupWorldFile = shutdown,
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
PerformWorldSave = shutdown,
SendAlerts = true,
@ -1387,7 +1441,11 @@ namespace ServerManagerTool.Windows
var task = Task.Run(() =>
{
app.PerformProfileShutdown(profile, false, false, false, false, token);
app.PerformProfileShutdown(profile, false, ServerUpdateType.None, false, false, token);
Task.Delay(5000).Wait();
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName));
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
@ -1403,15 +1461,22 @@ namespace ServerManagerTool.Windows
{
await Task.WhenAll(tasks);
}
catch { }
catch (OperationCanceledException)
{
AddCriticalBlockContent(_globalizer.GetResourceString("ServerMonitor_ProcessServer_CancelledLabel"));
}
catch (Exception)
{
}
finally
{
_currentProfileCommands.Clear();
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}
}
private async Task UpdateSelectedServersAsync()
private async Task UpdateSelectedServersAsync(bool updateModsOnly, bool processServersSequentially, int sequentialProcessDelay)
{
if (CancellationTokenSource != null)
return;
@ -1438,7 +1503,7 @@ namespace ServerManagerTool.Windows
// 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));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ServerName));
continue;
}
@ -1451,11 +1516,11 @@ namespace ServerManagerTool.Windows
case ServerStatus.Initializing:
case ServerStatus.Stopping:
case ServerStatus.Unknown:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ServerName, server.Runtime.StatusString));
continue;
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ServerName));
continue;
}
@ -1473,7 +1538,6 @@ namespace ServerManagerTool.Windows
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
@ -1493,7 +1557,11 @@ namespace ServerManagerTool.Windows
var task = Task.Run(() =>
{
app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, true, false, false, token);
app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, updateModsOnly ? ServerUpdateType.Mods : ServerUpdateType.ServerAndMods, false, false, token);
Task.Delay(5000).Wait();
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandComplete"), profile.ServerName));
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
@ -1506,9 +1574,16 @@ namespace ServerManagerTool.Windows
{
await Task.WhenAll(tasks);
}
catch { }
catch (OperationCanceledException)
{
AddCriticalBlockContent(_globalizer.GetResourceString("ServerMonitor_ProcessServer_CancelledLabel"));
}
catch (Exception)
{
}
finally
{
_currentProfileCommands.Clear();
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}

View file

@ -224,7 +224,7 @@ namespace ServerManagerTool
var app = new ServerApp(true)
{
CheckForOnlinePlayers = this.CheckForOnlinePlayers,
SendMessages = this.SendShutdownMessages,
SendShutdownMessages = this.SendShutdownMessages,
BackupWorldFile = this.BackupWorldFile,
ShutdownInterval = this.ShutdownInterval,
ShutdownReason = this.ShutdownReason,
@ -253,7 +253,7 @@ namespace ServerManagerTool
_shutdownCancellationSource = new CancellationTokenSource();
var exitCode = await Task.Run(() => app.PerformProfileShutdown(profile, restartServer, updateServer, false, CommonConfig.Default.SteamCmdRemoveQuit, _shutdownCancellationSource.Token));
var exitCode = await Task.Run(() => app.PerformProfileShutdown(profile, restartServer, updateServer ? ServerUpdateType.ServerAndMods : ServerUpdateType.None, false, CommonConfig.Default.SteamCmdRemoveQuit, _shutdownCancellationSource.Token));
if (exitCode != ServerApp.EXITCODE_NORMALEXIT && exitCode != ServerApp.EXITCODE_CANCELLED)
throw new ApplicationException($"An error occured during the shutdown process - ExitCode: {exitCode}");