Merge pull request #21 from Bletch1971/ServerMonitorChanges

Server monitor changes
This commit is contained in:
Brett Hewitson 2022-05-18 23:54:00 +10:00 committed by GitHub
commit bdd36bf235
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1825 additions and 329 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View file

@ -123,6 +123,16 @@
<PropertyGroup>
<SignManifests>false</SignManifests>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug - ServerMonitor|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug - ServerMonitor\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="DotNetZip, Version=1.13.8.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.13.8\lib\net40\DotNetZip.dll</HintPath>
@ -287,6 +297,7 @@
</Content>
<Resource Include="Art\Filter.ico" />
<Resource Include="Art\DropArrow.ico" />
<Resource Include="Art\Restart.ico" />
<Content Include="Globalization\en-US\en-US.xaml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View file

@ -400,6 +400,7 @@
<!--#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_ServerColumnLabel">Server</sys:String>
<sys:String x:Key="ServerMonitor_MapColumnLabel">Map</sys:String>
@ -409,6 +410,16 @@
<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_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>
@ -419,7 +430,23 @@
<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>
<sys:String x:Key="ServerMonitor_CloseWindow_ConfirmLabel">You are currently perform a server update, closing the window with disconnect you from steamcmd. Do you want to continue closing the window?</sys:String>
<sys:String x:Key="ServerMonitor_CloseWindow_ConfirmLabel">You are currently performing a server update, closing the window will disconnect you from steamcmd. Do you want to continue closing the window?</sys:String>
<sys:String x:Key="ServerMonitor_ShutdownServers_ConfirmTitle">Confirm Shutdown Servers</sys:String>
<sys:String x:Key="ServerMonitor_ShutdownServers_ConfirmLabel">You are about to shutdown the selected servers. Do you want to continue?</sys:String>
<sys:String x:Key="ServerMonitor_StartServers_ConfirmTitle">Confirm Start Servers</sys:String>
<sys:String x:Key="ServerMonitor_StartServers_ConfirmLabel">You are about to start the selected servers. Do you want to continue?</sys:String>
<sys:String x:Key="ServerMonitor_StopServers_ConfirmTitle">Confirm Stop Servers</sys:String>
<sys:String x:Key="ServerMonitor_StopServers_ConfirmLabel">You are about to stop the selected servers. Do you want to continue?</sys:String>
<sys:String x:Key="ServerMonitor_RestartServers_ConfirmTitle">Confirm Restart Servers</sys:String>
<sys:String x:Key="ServerMonitor_RestartServers_ConfirmLabel">You are about to restart the selected servers. Do you want to continue?</sys:String>
<sys:String x:Key="ServerMonitor_UpdateServers_ConfirmTitle">Confirm Update Servers</sys:String>
<sys:String x:Key="ServerMonitor_UpdateServers_ConfirmLabel">You are about to update the selected servers. Do you want to continue?</sys:String>
<sys:String x:Key="ServerMonitor_BackupServers_ConfirmTitle">Confirm Backup Servers</sys:String>
<sys:String x:Key="ServerMonitor_BackupServers_ConfirmLabel">You are about to backup the selected servers. Do you want to continue?</sys:String>
<sys:String x:Key="ServerMonitor_NoServersSelected_ErrorTitle">Selected Servers Error</sys:String>
<sys:String x:Key="ServerMonitor_NoServersSelected_ErrorLabel">You have not selected any servers. Selected one or more servers in the list and try again.</sys:String>
<sys:String x:Key="ServerMonitor_RunningProcesses_ConfirmTitle">Close Server Monitor Error</sys:String>
<sys:String x:Key="ServerMonitor_RunningProcesses_ConfirmLabel">The server monitor window cannot be closed at this time. One or more of the servers is currently starting, shutting down or restarting.</sys:String>
<!--#endregion-->
<!--#region Shutdown Window -->

View file

@ -102,6 +102,7 @@ namespace ServerManagerTool.Lib
public bool DeleteOldBackupFiles = Config.Default.AutoBackup_DeleteOldFiles;
public int ExitCode = EXITCODE_NORMALEXIT;
public bool OutputLogs = false;
public bool PerformWorldSave = Config.Default.ServerShutdown_EnableWorldSave;
public bool SendAlerts = false;
public bool SendEmails = false;
public string ShutdownReason = null;
@ -258,11 +259,14 @@ namespace ServerManagerTool.Lib
ServerStatusChangeCallback?.Invoke(ServerStatus.Stopped);
// make a backup of the current profile and config files.
CreateProfileBackupArchiveFile(_profile);
if (ServerProcess != ServerProcessType.Stop)
{
// make a backup of the current profile and config files.
CreateProfileBackupArchiveFile(_profile);
if (ExitCode != EXITCODE_NORMALEXIT)
return;
if (ExitCode != EXITCODE_NORMALEXIT)
return;
}
if (BackupWorldFile)
{
@ -285,10 +289,10 @@ namespace ServerManagerTool.Lib
{
ServerStatusChangeCallback?.Invoke(ServerStatus.Stopped);
}
}
if (ExitCode != EXITCODE_NORMALEXIT)
return;
if (ExitCode != EXITCODE_NORMALEXIT)
return;
}
// check if this is a shutdown only, or a shutdown and restart.
if (restartServer)
@ -579,7 +583,7 @@ namespace ServerManagerTool.Lib
// BH - commented out until funcom provide a way to send a save command
// check if we need to perform a world save
//if (serverAccessible && Config.Default.ServerShutdown_EnableWorldSave)
//if (serverAccessible && PerformWorldSave)
//{
// try
// {
@ -716,6 +720,7 @@ namespace ServerManagerTool.Lib
if (process.HasExited)
{
process.Close();
if (Config.Default.EmailNotify_ShutdownRestart)
SendEmail($"{_profile.ProfileName} server shutdown", $"The server has been shutdown to perform the {ServerProcess} process.", false);
}

View file

@ -58,11 +58,11 @@ namespace ServerManagerTool.Utils
return new List<string> { 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> { 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> { 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<string> ShutdownServer(string channelId, string profileIdOrAlias, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(profileIdOrAlias))
{
return new List<string> { string.Format(_globalizer.GetResourceString("DiscordBot_ProfileMissing"), CommandType.Shutdown) };
}
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.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<string> StopServer(string channelId, string profileIdOrAlias, CancellationToken token)
private static IList<string> 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;

View file

@ -4,8 +4,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:sm="clr-namespace:ServerManagerTool"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:controls="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common"
xmlns:enum="clr-namespace:ServerManagerTool.Enums"
xmlns:vm="clr-namespace:ServerManagerTool.Lib.ViewModel"
mc:Ignorable="d"
@ -19,7 +22,15 @@
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sm:ScrollToBottomAction x:Key="ScrollToBottomAction" />
<vm:MapNameValueConverter x:Key="MapNameValueConverter"/>
<DataTemplate x:Key="StopButtonContent">
<StackPanel Orientation="Horizontal">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Stop.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>
@ -28,11 +39,20 @@
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="*" MinHeight="200"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MinHeight="100"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
@ -41,12 +61,55 @@
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Shortcut.ico,Size=32}"/>
</Button>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
<Button Grid.Row="0" Grid.Column="1" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="SelectAllServers_Click" ToolTip="{DynamicResource ServerMonitor_SelectAllServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Checked.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="2" Width="22" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="UnselectAllServers_Click" ToolTip="{DynamicResource ServerMonitor_UnselectAllServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Unchecked.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="3" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding StartServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_StartServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Start.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="4" Width="22" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding RestartServersCommand}" CommandParameter="{Binding}" ToolTip="{DynamicResource ServerMonitor_RestartServersButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Restart.ico,Size=32}"/>
</Button>
<controls:DropDownButton Grid.Row="0" Grid.Column="5" Width="35" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Center">
<ContentControl ContentTemplate="{StaticResource StopButtonContent}" />
<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>
</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>
<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">
<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="2" 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="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.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
@ -61,7 +124,7 @@
</Button.Style>
</Button>
<Button Grid.Row="0" Grid.Column="3" 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="10" 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>
@ -78,7 +141,7 @@
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/ChangeNotes.ico,Size=32}"/>
</Button>
<DataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Margin="5" Name="ServersGrid" 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 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">
<DataGrid.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type DataGridCell}">
@ -140,6 +203,14 @@
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="{DynamicResource ServerMonitor_SelectedColumnLabel}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_ServerColumnLabel}" />
@ -530,11 +601,27 @@
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="11" 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">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged" >
<sm:ScrollToBottomAction IsEnabled="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</RichTextBox.Resources>
<FlowDocument Name="ConsoleContent"/>
</RichTextBox>
</Grid>
</DockPanel>
<!-- Drag and Drop Popup -->
<Popup Grid.Row="0" Grid.Column="0" x:Name="popup" IsHitTestVisible="False" Placement="RelativePoint" PlacementTarget="{Binding ElementName=ServerMonitorUI}" AllowsTransparency="True">
<Popup x:Name="popup" IsHitTestVisible="False" Placement="RelativePoint" PlacementTarget="{Binding ElementName=ServerMonitorUI}" AllowsTransparency="True">
<Border BorderBrush="LightSteelBlue" BorderThickness="2" Background="White" Opacity="0.75">
<StackPanel Orientation="Horizontal" Margin="4,3,8,3">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Drag.ico,Size=32}" Width="16" Height="16"/>
@ -544,7 +631,7 @@
</Popup>
<!-- It's important that this is in the end of the XAML as it needs to be on top of everything else! -->
<Grid x:Name="OverlayGrid" Visibility="Collapsed" DockPanel.Dock="Top" >
<Grid x:Name="OverlayGrid" Visibility="Collapsed" DockPanel.Dock="Top">
<Grid Background="Black" Opacity="0.5"/>
<Border MinWidth="250" Background="Orange" BorderBrush="Black" BorderThickness="1" CornerRadius="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>

View file

@ -2,6 +2,7 @@
using NLog;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.DiscordBot.Enums;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common;
@ -16,7 +17,9 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Windows
@ -26,19 +29,42 @@ namespace ServerManagerTool.Windows
/// </summary>
public partial class ServerMonitorWindow : Window
{
public class ServerMonitorOutput_Error : Run
{
public ServerMonitorOutput_Error(string value)
: base(value)
{
Foreground = Brushes.Red;
}
}
public class ServerMonitorOutput_Success : Run
{
public ServerMonitorOutput_Success(string value)
: base(value)
{
Foreground = Brushes.Green;
}
}
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly List<ServerMonitorWindow> Windows = new List<ServerMonitorWindow>();
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private CancellationTokenSource _upgradeCancellationSource = null;
private ActionQueue _versionChecker;
private ActionQueue _canExecuteChecker;
private readonly Dictionary<string, CommandType> _currentProfileCommands = new Dictionary<string, CommandType>();
private bool HasRunningCommands => _currentProfileCommands.Count > 0;
public static readonly DependencyProperty ServerManagerProperty = DependencyProperty.Register(nameof(ServerManager), typeof(ServerManager), typeof(ServerMonitorWindow), new PropertyMetadata(null));
public static readonly DependencyProperty LatestServerManagerVersionProperty = DependencyProperty.Register(nameof(LatestServerManagerVersion), typeof(Version), typeof(ServerMonitorWindow), new PropertyMetadata(new Version()));
public static readonly DependencyProperty ShowUpdateButtonProperty = DependencyProperty.Register(nameof(ShowUpdateButton), typeof(bool), typeof(ServerMonitorWindow), new PropertyMetadata(false));
public static readonly DependencyProperty IsStandAloneWindowProperty = DependencyProperty.Register(nameof(IsStandAloneWindow), typeof(bool), typeof(ServerMonitorWindow), new PropertyMetadata(false));
public static readonly DependencyProperty CancellationTokenSourceProperty = DependencyProperty.Register(nameof(CancellationTokenSource), typeof(CancellationTokenSource), typeof(ServerMonitorWindow));
public ServerMonitorWindow() : this(null)
public ServerMonitorWindow(): this(null)
{
}
@ -89,6 +115,12 @@ namespace ServerManagerTool.Windows
set { SetValue(IsStandAloneWindowProperty, value); }
}
public CancellationTokenSource CancellationTokenSource
{
get { return (CancellationTokenSource)GetValue(CancellationTokenSourceProperty); }
set { SetValue(CancellationTokenSourceProperty, value); }
}
private void ServerMonitorWindow_Loaded(object sender, RoutedEventArgs e)
{
if (ServerManager == null)
@ -121,6 +153,9 @@ namespace ServerManagerTool.Windows
_versionChecker = new ActionQueue();
_versionChecker.PostAction(CheckForUpdates).DoNotWait();
}
_canExecuteChecker = new ActionQueue();
_canExecuteChecker.PostAction(RaiseCanExecuteChanged).DoNotWait();
}
private void ServerMonitorWindow_LocationChanged(object sender, EventArgs e)
@ -156,6 +191,14 @@ namespace ServerManagerTool.Windows
protected override void OnClosing(CancelEventArgs e)
{
if (HasRunningCommands)
{
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_RunningProcesses_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_RunningProcesses_ConfirmTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
e.Cancel = true;
return;
}
if (this.OwnedWindows.OfType<ProgressWindow>().Any())
{
if (MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_CloseWindow_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_CloseWindow_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
@ -166,7 +209,9 @@ namespace ServerManagerTool.Windows
}
Windows.Remove(this);
_versionChecker?.DisposeAsync().DoNotWait();
_canExecuteChecker?.DisposeAsync().DoNotWait();
base.OnClosing(e);
}
@ -409,6 +454,29 @@ namespace ServerManagerTool.Windows
}
}
public void AddErrorBlockContent(string message)
{
var p = new Paragraph();
p.Inlines.Add(new ServerMonitorOutput_Error(message));
ConsoleContent.Blocks.Add(p);
}
public void AddMessageBlockContent(string message)
{
var p = new Paragraph();
p.Inlines.Add(new ServerMonitorOutput_Success(message));
ConsoleContent.Blocks.Add(p);
}
public void ClearBlockContents()
{
ConsoleContent.Blocks.Clear();
}
private async Task CheckForUpdates()
{
string url = App.Instance.BetaVersion ? Config.Default.LatestServerManagerBetaVersionUrl : Config.Default.LatestServerManagerVersionUrl;
@ -455,6 +523,14 @@ namespace ServerManagerTool.Windows
return new ServerMonitorWindow(serverManager);
}
public async Task RaiseCanExecuteChanged()
{
await TaskUtils.RunOnUIThreadAsync(() => CommandManager.InvalidateRequerySuggested());
await Task.Delay(5000);
_canExecuteChecker?.PostAction(RaiseCanExecuteChanged).DoNotWait();
}
private void SetWindowTitle()
{
if (!string.IsNullOrWhiteSpace(App.Instance.Title))
@ -699,6 +775,114 @@ namespace ServerManagerTool.Windows
}
}
public ICommand BackupServersCommand
{
get
{
return new RelayCommand<object>(
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<object>(
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<object>(
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<object>(
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<object>(
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<object>(
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));
@ -813,5 +997,521 @@ namespace ServerManagerTool.Windows
}
#endregion
private void SelectAllServers_Click(object sender, RoutedEventArgs e)
{
foreach (var server in ServerManager.Servers)
{
server.Selected = true;
}
}
private void UnselectAllServers_Click(object sender, RoutedEventArgs e)
{
foreach (var server in ServerManager.Servers)
{
server.Selected = false;
}
}
private async Task BackupSelectedServersAsync()
{
if (CancellationTokenSource != null)
return;
var serverList = ServerManager.Servers.Where(s => s.Selected);
if (serverList.IsEmpty())
{
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_BackupServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_BackupServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
ClearBlockContents();
var profileList = new List<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:
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Backup);
profileList.Add(ServerProfileSnapshot.Create(server.Profile));
}
CancellationTokenSource = new CancellationTokenSource();
var token = CancellationTokenSource.Token;
var tasks = new List<Task>();
foreach (var profile in profileList)
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
ServerProcess = ServerProcessType.Backup,
ServerStatusChangeCallback = (ServerStatus serverStatus) =>
{
TaskUtils.RunOnUIThreadAsync(() =>
{
var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase));
if (server != null)
{
server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown);
}
}).Wait(token);
}
};
var task = Task.Run(() =>
{
app.PerformProfileBackup(profile, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
tasks.Add(task);
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_BackupRequested"), profile.ServerName));
}
try
{
await Task.WhenAll(tasks);
}
catch { }
finally
{
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}
}
private async Task RestartSelectedServersAsync()
{
if (CancellationTokenSource != null)
return;
var serverList = ServerManager.Servers.Where(s => s.Selected);
if (serverList.IsEmpty())
{
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_RestartServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_RestartServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
ClearBlockContents();
var profileList = new List<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()
{
if (CancellationTokenSource != null)
return;
var serverList = ServerManager.Servers.Where(s => s.Selected);
if (serverList.IsEmpty())
{
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_StartServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_StartServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
ClearBlockContents();
var profileList = new List<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.Running:
case ServerStatus.Uninstalled:
case ServerStatus.Unknown:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
continue;
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Start);
var profile = ServerProfileSnapshot.Create(server.Profile);
profile.AutoRestartIfShutdown = true;
profileList.Add(profile);
}
CancellationTokenSource = new CancellationTokenSource();
var token = CancellationTokenSource.Token;
var tasks = new List<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_StartRequested"), profile.ServerName));
}
try
{
await Task.WhenAll(tasks);
}
catch { }
finally
{
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}
}
private async Task StopSelectedServersAsync(bool shutdown)
{
if (CancellationTokenSource != null)
return;
var serverList = ServerManager.Servers.Where(s => s.Selected);
if (serverList.IsEmpty())
{
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = shutdown
? MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_ShutdownServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_ShutdownServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question)
: MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_StopServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
ClearBlockContents();
var profileList = new List<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.Stopped:
case ServerStatus.Uninstalled:
case ServerStatus.Unknown:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
continue;
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Stop);
profileList.Add(ServerProfileSnapshot.Create(server.Profile));
}
CancellationTokenSource = new CancellationTokenSource();
var token = CancellationTokenSource.Token;
var tasks = new List<Task>();
foreach (var profile in profileList)
{
var app = new ServerApp(true)
{
BackupWorldFile = shutdown,
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
PerformWorldSave = shutdown,
SendAlerts = true,
SendEmails = false,
ServerProcess = shutdown ? ServerProcessType.Shutdown : ServerProcessType.Stop,
ServerStatusChangeCallback = (ServerStatus serverStatus) =>
{
TaskUtils.RunOnUIThreadAsync(() =>
{
var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase));
if (server != null)
{
server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown);
}
}).Wait(token);
}
};
if (!shutdown)
app.ShutdownInterval = 0;
var task = Task.Run(() =>
{
app.PerformProfileShutdown(profile, false, false, false, false, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
tasks.Add(task);
if (shutdown)
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ShutdownRequested"), profile.ServerName));
else
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_StopRequested"), profile.ServerName));
}
try
{
await Task.WhenAll(tasks);
}
catch { }
finally
{
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}
}
private async Task UpdateSelectedServersAsync()
{
if (CancellationTokenSource != null)
return;
var serverList = ServerManager.Servers.Where(s => s.Selected);
if (serverList.IsEmpty())
{
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorLabel"), _globalizer.GetResourceString("ServerMonitor_NoServersSelected_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_UpdateServers_ConfirmLabel"), _globalizer.GetResourceString("ServerMonitor_UpdateServers_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
ClearBlockContents();
var profileList = new List<ServerProfileSnapshot>();
foreach (var server in serverList)
{
var performRestart = false;
// check if another command is being run against the profile
if (_currentProfileCommands.ContainsKey(server.Profile.ProfileID))
{
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_CommandRunningProfile"), _currentProfileCommands[server.Profile.ProfileID], server.Profile.ProfileName));
continue;
}
switch (server.Runtime.Status)
{
case ServerStatus.Running:
performRestart = true;
break;
case ServerStatus.Initializing:
case ServerStatus.Stopping:
case ServerStatus.Unknown:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileBadStatus"), server.Profile.ProfileName, server.Runtime.StatusString));
continue;
case ServerStatus.Updating:
AddErrorBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_ProfileUpdating"), server.Profile.ProfileName));
continue;
}
_currentProfileCommands.Add(server.Profile.ProfileID, CommandType.Update);
var profile = ServerProfileSnapshot.Create(server.Profile);
profile.RestartAfterShutdown1 = performRestart; // use this property to trigger a restart
profileList.Add(profile);
}
CancellationTokenSource = new CancellationTokenSource();
var token = CancellationTokenSource.Token;
var tasks = new List<Task>();
foreach (var profile in profileList)
{
var app = new ServerApp(true)
{
DeleteOldBackupFiles = !Config.Default.AutoBackup_EnableBackup,
OutputLogs = false,
SendAlerts = true,
SendEmails = false,
ServerProcess = ServerProcessType.Update,
ServerStatusChangeCallback = (ServerStatus serverStatus) =>
{
TaskUtils.RunOnUIThreadAsync(() =>
{
var server = ServerManager.Instance.Servers.FirstOrDefault(s => string.Equals(profile.ProfileId, s.Profile.ProfileID, StringComparison.OrdinalIgnoreCase));
if (server != null)
{
server.Runtime.UpdateServerStatus(serverStatus, serverStatus != ServerStatus.Unknown);
}
}).Wait(token);
}
};
var task = Task.Run(() =>
{
app.PerformProfileShutdown(profile, profile.RestartAfterShutdown1, true, false, false, token);
_currentProfileCommands.Remove(profile.ProfileId);
}, token);
tasks.Add(task);
AddMessageBlockContent(string.Format(_globalizer.GetResourceString("DiscordBot_UpdateRequested"), profile.ServerName));
}
try
{
await Task.WhenAll(tasks);
}
catch { }
finally
{
CancellationTokenSource?.Dispose();
CancellationTokenSource = null;
}
}
}
}