source code checkin

This commit is contained in:
Brett Hewitson 2021-01-07 16:23:23 +10:00
parent 5f8fb2c825
commit 7e57b72e35
675 changed files with 168433 additions and 0 deletions

View file

@ -0,0 +1,31 @@
<Window x:Class="ServerManagerTool.AddUserWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinHeight="200" Width="300" Height="200" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" ResizeMode="CanResize"
Icon="../Art/favicon.ico" Title="{DynamicResource AddUser_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Users, Mode=TwoWay}" Margin="5,5,5,0" TextWrapping="NoWrap" VerticalScrollBarVisibility="Auto" AcceptsReturn="true"/>
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="5,5,5,0" Text="{DynamicResource AddUser_InstructionLabel}" VerticalAlignment="Center" TextWrapping="Wrap" />
<Button Grid.Row="2" Grid.Column="0" Content="{DynamicResource AddUser_ProcessButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Right" Click="Process_Click"/>
<Button Grid.Row="2" Grid.Column="1" Content="{DynamicResource AddUser_CancelButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" IsCancel="True"/>
</Grid>
</Window>

View file

@ -0,0 +1,33 @@
using ServerManagerTool.Common.Utils;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for AddUserWindow.xaml
/// </summary>
public partial class AddUserWindow : Window
{
public AddUserWindow()
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.DataContext = this;
}
public static readonly DependencyProperty UsersProperty = DependencyProperty.Register(nameof(Users), typeof(string), typeof(AddUserWindow), new PropertyMetadata(string.Empty));
public string Users
{
get { return (string)GetValue(UsersProperty); }
set { SetValue(UsersProperty, value); }
}
private void Process_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
}
}

View file

@ -0,0 +1,23 @@
<Window x:Class="ServerManagerTool.AutoUpdateWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="380" WindowStyle="ToolWindow" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" SizeToContent="Height"
Loaded="Window_Loaded" Closing="Window_Closing"
Icon="../Art/favicon.ico" Title="{DynamicResource AutoUpdater_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<StackPanel HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" Width="Auto">
<ProgressBar x:Name="CompletionProgress" Height="10"/>
<Label x:Name="StatusLabel" Content="{DynamicResource AutoUpdater_Status}" HorizontalContentAlignment="Center"/>
<Button Content="{DynamicResource AutoUpdater_CancelButtonLabel}" Width="75" HorizontalAlignment="Center" IsCancel="True" Click="Button_Click"/>
</StackPanel>
</Grid>
</Window>

View file

@ -0,0 +1,65 @@
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using System;
using System.Threading;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for AutoUpdateWindow.xaml
/// </summary>
public partial class AutoUpdateWindow : Window
{
private GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private SteamCmdUpdater updater = new SteamCmdUpdater();
private CancellationTokenSource cancelSource;
public AutoUpdateWindow()
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
cancelSource = new CancellationTokenSource();
updater.UpdateSteamCmdAsync(Config.Default.DataPath, new Progress<SteamCmdUpdater.Update>(async u =>
{
var message = string.IsNullOrWhiteSpace(u.StatusKey) ? string.Empty : _globalizer.GetResourceString(u.StatusKey) ?? u.StatusKey;
this.StatusLabel.Content = message;
this.CompletionProgress.Value = u.CompletionPercent;
if(u.FailureText != null)
{
// TODO: Report error through UI
throw new Exception(u.FailureText);
}
if (u.CompletionPercent >= 100 || u.Cancelled)
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
var mainWindow = new MainWindow();
mainWindow.Show();
this.Close();
});
}
}), cancelSource.Token);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (cancelSource != null)
cancelSource.Cancel();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (cancelSource != null)
cancelSource.Cancel();
}
}
}

View file

@ -0,0 +1,24 @@
<Window x:Class="ServerManagerTool.CommandLineWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinWidth="500" MinHeight="200" Width="500" Height="200" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" ResizeMode="CanResize"
Icon="../Art/favicon.ico" Title="{DynamicResource CommandLine_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Name="OutputTextBox" Grid.Row="0" Text="{Binding Mode=OneWay}" Margin="5,5,5,0" IsReadOnly="True" IsReadOnlyCaretVisible="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"/>
<Button Grid.Row="1" Content="{DynamicResource CommandLine_CopyButtonLabel}" Margin="5" HorizontalAlignment="Center" Click="CopyToClipboard_Click"/>
</Grid>
</Window>

View file

@ -0,0 +1,41 @@
using ServerManagerTool.Common.Utils;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for CommandLineWindow.xaml
/// </summary>
public partial class CommandLineWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public CommandLineWindow(string commandLine)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.DataContext = commandLine;
}
public TextWrapping OutputTextWrapping
{
get { return OutputTextBox.TextWrapping; }
set { OutputTextBox.TextWrapping = value; }
}
private void CopyToClipboard_Click(object sender, RoutedEventArgs e)
{
try
{
System.Windows.Clipboard.SetText(this.DataContext as string);
MessageBox.Show(_globalizer.GetResourceString("CommandLine_CopyButton_ConfirmLabel"), _globalizer.GetResourceString("CommandLine_CopyButton_ConfirmTitle"), MessageBoxButton.OK);
}
catch
{
MessageBox.Show(_globalizer.GetResourceString("CommandLine_CopyButton_ErrorLabel"), _globalizer.GetResourceString("CommandLine_CopyButton_ErrorTitle"), MessageBoxButton.OK);
}
}
}
}

View file

@ -0,0 +1,128 @@
<Window x:Class="ServerManagerTool.GameDataWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
MinWidth="640" MinHeight="480" Width="800" Height="480" ResizeMode="CanResize" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Loaded="Window_Loaded"
Icon="../Art/favicon.ico" Title="{DynamicResource GameDataWindow_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Button Width="22" Height="22" Click="AddGameData_Click" ToolTip="{DynamicResource GameDataWindow_AddGameDataTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Add.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="ClearGameData_Click" Margin="10,0,0,0" ToolTip="{DynamicResource GameDataWindow_ClearGameDataTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="ReloadGameData_Click" Margin="10,0,0,0" ToolTip="{DynamicResource GameDataWindow_ReloadGameDataTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Reload.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="ValidateGameData_Click" Margin="10,0,0,0" ToolTip="{DynamicResource GameDataWindow_ValidateGameDataTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Validate.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="OpenGameDataFolder_Click" Margin="10,0,0,0" ToolTip="{DynamicResource GameDataWindow_OpenGameDataFolderTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/FolderOpen.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="GameDataForum_Click" Margin="20,0,0,0" ToolTip="{DynamicResource GameDataWindow_GameDataForumTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Help.ico,Size=32}"/>
</Button>
</StackPanel>
</GroupBox.Header>
<DataGrid ItemsSource="{Binding GameDataFiles}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserSortColumns="true" SelectionMode="Single" CanUserResizeColumns="False" CanUserResizeRows="False" RowHeaderWidth="25">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding IsUserData}" Value="False">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
<DataTrigger Binding="{Binding HasError}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridTextColumn Width="4*" Binding="{Binding FileName, Mode=OneWay}">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource GameDataWindow_NameColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="Auto" MinWidth="80" Binding="{Binding Version, Mode=OneWay}">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource GameDataWindow_VersionColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="130" Binding="{Binding CreatedDate, Mode=OneWay}" Header="{DynamicResource GameDataWindow_DateColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="RemoveGameData_Click" ToolTip="{DynamicResource GameDataWindow_RemoveGameDataTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsUserData}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
</Window>

View file

@ -0,0 +1,371 @@
using Microsoft.WindowsAPICodePack.Dialogs;
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Lib;
using ServerManagerTool.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for GameDataWindow.xaml
/// </summary>
public partial class GameDataWindow : Window
{
public class GameDataFileList : SortableObservableCollection<GameDataFile>
{
public override string ToString()
{
return $"{nameof(GameDataFile)} - {Count}";
}
}
public class GameDataFile : DependencyObject
{
public static readonly DependencyProperty CreatedDateProperty = DependencyProperty.Register(nameof(CreatedDate), typeof(DateTime), typeof(GameDataFile), new PropertyMetadata(DateTime.MinValue));
public static readonly DependencyProperty FileProperty = DependencyProperty.Register(nameof(File), typeof(string), typeof(GameDataFile), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register(nameof(FileName), typeof(string), typeof(GameDataFile), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsUserDataProperty = DependencyProperty.Register(nameof(IsUserData), typeof(bool), typeof(GameDataFile), new PropertyMetadata(true));
public static readonly DependencyProperty VersionProperty = DependencyProperty.Register(nameof(Version), typeof(string), typeof(GameDataFile), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty HasErrorProperty = DependencyProperty.Register(nameof(HasError), typeof(bool), typeof(GameDataFile), new PropertyMetadata(false));
public DateTime CreatedDate
{
get { return (DateTime)GetValue(CreatedDateProperty); }
set { SetValue(CreatedDateProperty, value); }
}
public string File
{
get { return (string)GetValue(FileProperty); }
set { SetValue(FileProperty, value); }
}
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
public bool IsUserData
{
get { return (bool)GetValue(IsUserDataProperty); }
set { SetValue(IsUserDataProperty, value); }
}
public string Version
{
get { return (string)GetValue(VersionProperty); }
set { SetValue(VersionProperty, value); }
}
public bool HasError
{
get { return (bool)GetValue(HasErrorProperty); }
set { SetValue(HasErrorProperty, value); }
}
}
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty GameDataFilesProperty = DependencyProperty.Register(nameof(GameDataFiles), typeof(GameDataFileList), typeof(GameDataWindow), new PropertyMetadata(null));
public GameDataWindow()
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.GameDataFiles = new GameDataFileList();
this.DataContext = this;
}
public GameDataFileList GameDataFiles
{
get { return GetValue(GameDataFilesProperty) as GameDataFileList; }
set { SetValue(GameDataFilesProperty, value); }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
ReloadGameDataFiles();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_LoadErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void AddGameData_Click(object sender, RoutedEventArgs e)
{
var dialog = new CommonOpenFileDialog();
dialog.Title = GlobalizedApplication.Instance.GetResourceString("GameDataWindow_AddDialogTitle");
dialog.DefaultExtension = GlobalizedApplication.Instance.GetResourceString("GameDataWindow_GameDataDefaultExtension");
dialog.Filters.Add(new CommonFileDialogFilter(GlobalizedApplication.Instance.GetResourceString("GameDataWindow_AddFilterLabel"), GlobalizedApplication.Instance.GetResourceString("GameDataWindow_AddFilterExtension")));
if (dialog == null || dialog.ShowDialog(this) != CommonFileDialogResult.Ok)
return;
try
{
AddGameDataFile(GameData.UserDataFolder, dialog.FileName);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ClearGameData_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show(_globalizer.GetResourceString("GameDataWindow_ClearLabel"), _globalizer.GetResourceString("GameDataWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
try
{
if (!GameData.UserDataFolder.Equals(GameData.MainDataFolder, StringComparison.OrdinalIgnoreCase))
{
DeleteAllGameDataFiles(GameData.UserDataFolder);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void OpenGameDataFolder_Click(object sender, RoutedEventArgs e)
{
try
{
if (!Directory.Exists(GameData.UserDataFolder))
Directory.CreateDirectory(GameData.UserDataFolder);
Process.Start("explorer.exe", GameData.UserDataFolder);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_OpenErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void GameDataForum_Click(object sender, RoutedEventArgs e)
{
Process.Start(Config.Default.GameDataUrl);
}
private void ReloadGameData_Click(object sender, RoutedEventArgs e)
{
try
{
ReloadGameDataFiles();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_LoadErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void RemoveGameData_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show(_globalizer.GetResourceString("GameDataWindow_DeleteLabel"), _globalizer.GetResourceString("GameDataWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
try
{
var gameDataItem = ((GameDataFile)((Button)e.Source).DataContext);
DeleteGameDataFile(gameDataItem.File, true);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ValidateGameData_Click(object sender, RoutedEventArgs e)
{
try
{
ValidateAllGameDataFiles(GameData.UserDataFolder);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("GameDataWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void AddGameDataFile(string folder, string gameDataFile)
{
if (string.IsNullOrWhiteSpace(folder))
return;
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
var newGameDataFile = Path.Combine(folder, $"{Path.GetFileName(gameDataFile)}");
if (File.Exists(newGameDataFile))
throw new Exception(_globalizer.GetResourceString("GameDataWindow_ExistingFileErrorLabel"));
ValidateGameDataFile(gameDataFile);
File.Copy(gameDataFile, newGameDataFile, true);
LoadGameDataFile(newGameDataFile, true);
}
private void DeleteAllGameDataFiles(string folder)
{
if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder))
return;
var fileList = Directory.GetFiles(folder, $"*.{GlobalizedApplication.Instance.GetResourceString("GameDataWindow_GameDataDefaultExtension")}");
foreach (var file in fileList)
{
DeleteGameDataFile(file, false);
}
LoadGameDataFiles(GameData.UserDataFolder, true, true);
LoadGameDataFiles(GameData.MainDataFolder, false, false);
}
private void DeleteGameDataFile(string file, bool updateList)
{
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
return;
File.Delete(file);
if (updateList)
{
var gameDataFiles = GameDataFiles.Where(f => f.File == file).ToList();
foreach (var gameDataFile in gameDataFiles)
{
GameDataFiles.Remove(gameDataFile);
}
}
}
private void LoadGameDataFile(string file, bool isUserData)
{
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
return;
BaseGameData baseGameData = null;
try
{
baseGameData = BaseGameData.Load(file);
}
catch
{
// do nothing, just swallow the error
}
var gameDataFile = new GameDataFile
{
CreatedDate = baseGameData?.Created ?? DateTime.MinValue,
File = file,
FileName = string.IsNullOrWhiteSpace(file) ? string.Empty : Path.GetFileNameWithoutExtension(file),
IsUserData = isUserData,
Version = baseGameData?.Version ?? "0.0.0",
HasError = baseGameData == null,
};
GameDataFiles.Add(gameDataFile);
}
private void LoadGameDataFiles(string folder, bool isUserData, bool ClearExisting)
{
if (ClearExisting)
GameDataFiles.Clear();
if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder))
return;
var files = Directory.GetFiles(folder, $"*.{GlobalizedApplication.Instance.GetResourceString("GameDataWindow_GameDataDefaultExtension")}");
foreach (var file in files)
{
LoadGameDataFile(file, isUserData);
}
}
private void ReloadGameDataFiles()
{
if (!GameData.UserDataFolder.Equals(GameData.MainDataFolder, StringComparison.OrdinalIgnoreCase))
{
LoadGameDataFiles(GameData.UserDataFolder, true, true);
}
LoadGameDataFiles(GameData.MainDataFolder, false, false);
}
private void ValidateAllGameDataFiles(string folder)
{
if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder))
{
MessageBox.Show(_globalizer.GetResourceString("GameDataWindow_ValidateSuccessLabel"), _globalizer.GetResourceString("GameDataWindow_ValidateSuccessTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var fileList = Directory.GetFiles(folder, $"*.{GlobalizedApplication.Instance.GetResourceString("GameDataWindow_GameDataDefaultExtension")}");
var errorList = new List<string>();
foreach (var file in fileList)
{
try
{
ValidateGameDataFile(file);
}
catch (Exception ex)
{
errorList.Add($"{Path.GetFileNameWithoutExtension(file)} - {ex.Message}");
}
}
if (errorList.Count > 0)
{
var message = $"{_globalizer.GetResourceString("GameDataWindow_ValidateErrorLabel")}{Environment.NewLine}{string.Join(Environment.NewLine, errorList)}";
var window = new CommandLineWindow(message);
window.OutputTextWrapping = TextWrapping.NoWrap;
window.Height = 300;
window.Width = 600;
window.Title = _globalizer.GetResourceString("GameDataWindow_ValidateErrorTitle");
window.Owner = this;
window.ShowDialog();
}
else
{
MessageBox.Show(_globalizer.GetResourceString("GameDataWindow_ValidateSuccessLabel"), _globalizer.GetResourceString("GameDataWindow_ValidateSuccessTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void ValidateGameDataFile(string file)
{
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
return;
MainGameData gameData = null;
try
{
gameData = MainGameData.Load(file, true);
}
catch (Exception ex)
{
var message = _globalizer.GetResourceString("GameDataWindow_ValidateErrorMessage");
throw new Exception(message, ex);
}
if (gameData == null)
{
var message = _globalizer.GetResourceString("GameDataWindow_ValidateErrorMessage");
throw new Exception(message);
}
}
}
}

View file

@ -0,0 +1,692 @@
<UserControl x:Class="ServerManagerTool.GlobalSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cctl="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:gctl="clr-namespace:WPFSharp.Globalizer.Controls;assembly=WPFSharp.Globalizer"
mc:Ignorable="d"
d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:InvertBooleanConverter x:Key="InvertBooleanConverter"/>
<cc:MinutesToTimeValueConverter x:Key="MinutesToTimeValueConverter"/>
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
</Style>
<Style x:Key="HiddenTextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="Wheat"/>
<Setter Property="IsReadOnly" Value="True"/>
</Style>
<ContentControl x:Key="SaveManagerPasswordButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_SaveManagerPasswordButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="CreateManagerUserButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_CreateManagerUserButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="DeleteManagerUserButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_DeleteManagerUserButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="DataDirectoryButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_SetLocationButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="BackupDirectoryButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_SetLocationButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="ClearButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_ClearButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="SteamFileButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_SetSteamFileButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="SteamAPIKeyButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_SteamAPIKeyButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="SteamAPIKeyHelpButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_SteamAPIKeyHelpButtonLabel}" VerticalAlignment="Center" Margin="0,-2,0,-2"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="CacheDirectoryButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource GlobalSettings_CacheDirectoryButtonLabel}" VerticalAlignment="Center" Margin="0,-3,0,-3"/>
</StackPanel>
</ContentControl>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="3">
<ScrollViewer VerticalScrollBarVisibility="Visible">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Row="0" Grid.ColumnSpan="4" LastChildFill="False">
<Label DockPanel.Dock="Left" Content="{DynamicResource GlobalSettings_Title}" FontSize="20" VerticalAlignment="Center" />
<Button DockPanel.Dock="Right" Margin="10,0,0,0" Padding="5,2,5,2" Content="{DynamicResource GlobalSettings_ResetButtonLabel}" Click="ResetSettings_Click" VerticalAlignment="Center" ToolTip="{DynamicResource GlobalSettings_ResetButtonTooltip}"/>
<Label DockPanel.Dock="Right" Content="{Binding Version}" FontSize="15" VerticalAlignment="Center"/>
<Label DockPanel.Dock="Right" Content="{DynamicResource GlobalSettings_VersionLabel}" FontSize="15" VerticalAlignment="Center" />
</DockPanel>
<CheckBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" Content="{DynamicResource GlobalSettings_RunAsAdministratorLabel}" IsChecked="{Binding Config.RunAsAdministratorPrompt, Mode=TwoWay}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Margin="5" Content="{DynamicResource GlobalSettings_MinimizeToTrayLabel}" IsChecked="{Binding Config.MainWindow_MinimizeToTray, Mode=TwoWay}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" Content="{DynamicResource GlobalSettings_ManageFirewallLabel}" IsChecked="{Binding Config.ManageFirewallAutomatically, Mode=TwoWay}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2" Margin="5" Content="{DynamicResource GlobalSettings_ManagePublicIPLabel}" IsChecked="{Binding CurrentConfig.ManagePublicIPAutomatically, Mode=TwoWay}" HorizontalAlignment="Left"/>
<Label Grid.Row="3" Grid.Column="0" Margin="1" Content="{DynamicResource GlobalSettings_DataDirectoryLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Margin="1" Text="{Binding Config.DataPath, Mode=TwoWay}" IsReadOnly="True" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" />
<Button Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" Margin="5,1,0,1" VerticalAlignment="Center" HorizontalAlignment="Left" Content="{DynamicResource DataDirectoryButtonContent}" Click="SetDataDir_Click"/>
<Label Grid.Row="5" Grid.Column="0" Margin="1" Content="{DynamicResource GlobalSettings_BackupDirectoryLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2" Margin="1" Text="{Binding Config.BackupPath, Mode=TwoWay}" IsReadOnly="True" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" />
<StackPanel Grid.Row="5" Grid.Column="3" Orientation="Horizontal">
<Button Margin="5,1,0,1" VerticalAlignment="Center" Content="{DynamicResource BackupDirectoryButtonContent}" Click="SetBackupDir_Click"/>
<Button Margin="5,1,0,1" VerticalAlignment="Center" Content="{DynamicResource ClearButtonContent}" Click="ClearBackupDir_Click"/>
</StackPanel>
<GroupBox Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_LanguageSelectionLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<gctl:LanguageSelectionComboBox Grid.Row="0" Grid.Column="0" Margin="0,0,5,0" SelectionChanged="LanguageSelectionComboBox_SelectionChanged"/>
<StackPanel Grid.Row="0" Grid.Column="1" Margin="5,0,0,0" Orientation="Horizontal">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TranslatedByLabel, Path=Content}" Value="unknown">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label Content="{DynamicResource Generic_TranslatedByLabel}" VerticalAlignment="Center"/>
<Label Name="TranslatedByLabel" Content="{DynamicResource Generic_TranslatedBy}" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_SteamSettingsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Margin="5" IsChecked="{Binding Config.SteamCmd_UseAnonymousCredentials}" Content="{DynamicResource GlobalSettings_SteamCmdCredentialsLabel}" VerticalAlignment="Bottom" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_SteamCmdCredentialsTooltip}" HorizontalAlignment="Left">
<CheckBox.Style>
<Style BasedOn="{StaticResource {x:Type CheckBox}}" TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Config.SteamCmdRedirectOutput}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<CheckBox Grid.Row="2" Grid.Column="4" Grid.ColumnSpan="4" Margin="5" Content="{DynamicResource GlobalSettings_SteamCmdRedirectOutputLabel}" IsChecked="{Binding Config.SteamCmdRedirectOutput, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_SteamCmdRedirectOutputTooltip}" HorizontalAlignment="Left" Visibility="Collapsed"/>
<Label Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="9" Content="{DynamicResource GlobalSettings_SteamCMDAuthentication_DisabledTooltip}" VerticalAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Config.SteamCmdRedirectOutput}" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Label Grid.Row="4" Grid.Column="0" Margin="1" Content="{DynamicResource GlobalSettings_SteamCmdUsernameLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="4" Grid.Column="1" Margin="1" Text="{Binding Config.SteamCmd_Username}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_SteamCmdUsernameTooltip}">
<TextBox.Style>
<Style BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Config.SteamCmdRedirectOutput}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
<DataTrigger Binding="{Binding Config.SteamCmd_UseAnonymousCredentials}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Label Grid.Row="4" Grid.Column="3" Margin="1" Content="{DynamicResource GlobalSettings_SteamCmdPasswordLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="4" Grid.Column="4" Margin="1" Name="HideSteamPasswordTextBox" Text="{DynamicResource ServerSettings_HidePasswordText}" ToolTip="{DynamicResource ServerSettings_HidePasswordTooltip}" GotFocus="HiddenField_GotFocus">
<TextBox.Style>
<Style BasedOn="{StaticResource HiddenTextBoxStyle}" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Config.SteamCmdRedirectOutput}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
<DataTrigger Binding="{Binding Config.SteamCmd_UseAnonymousCredentials}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox Grid.Row="4" Grid.Column="4" Margin="1" Name="SteamPasswordTextBox" Text="{Binding Config.SteamCmd_Password, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_SteamCmdPasswordTooltip}" LostFocus="HiddenField_LostFocus" VerticalContentAlignment="Center" Visibility="Collapsed">
<TextBox.Style>
<Style BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Config.SteamCmdRedirectOutput}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
<DataTrigger Binding="{Binding Config.SteamCmd_UseAnonymousCredentials}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Grid.Row="4" Grid.Column="6" Margin="1" Content="{DynamicResource GlobalSettings_SteamCMDAuthenticateButtonLabel}" Click="SteamCMDAuthenticate_Click">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Config.SteamCmdRedirectOutput}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
<DataTrigger Binding="{Binding Config.SteamCmd_UseAnonymousCredentials}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Label Grid.Row="5" Grid.Column="0" Margin="1" Content="{DynamicResource GlobalSettings_SteamAPIKeyLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="4" Margin="1" Name="HideSteamAPIKeyTextBox" Text="{DynamicResource ServerSettings_HidePasswordText}" ToolTip="{DynamicResource ServerSettings_HidePasswordTooltip}" GotFocus="HiddenField_GotFocus" Style="{StaticResource HiddenTextBoxStyle}"/>
<TextBox Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="4" Margin="1" Name="SteamAPIKeyTextBox" Text="{Binding CommonConfig.SteamAPIKey, Mode=TwoWay}" LostFocus="HiddenField_LostFocus" VerticalContentAlignment="Center" Visibility="Collapsed"/>
<Button Grid.Row="5" Grid.Column="6" Margin="1" Content="{DynamicResource SteamAPIKeyButtonContent}" Click="ApplySteamAPIKey_Click"/>
<Button Grid.Row="5" Grid.Column="7" Margin="1" Content="{DynamicResource SteamAPIKeyHelpButtonContent}" Click="SteamAPIKeyHelp_Click" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_ServerStatusLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_EnableServerStatusActionsLabel}" IsChecked="{Binding Config.ServerStatus_EnableActions, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_EnableServerStatusActionsTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,0,0,5" Content="{DynamicResource GlobalSettings_ShowServerStatusActionConfirmationLabel}" IsChecked="{Binding Config.ServerStatus_ShowActionConfirmation, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ShowServerStatusActionConfirmationTooltip}" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="12" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_ServerStartupLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_ValidateProfileOnServerStartLabel}" IsChecked="{Binding Config.ValidateProfileOnServerStart, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ValidateProfileOnServerStartTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,0,0,5" Content="{DynamicResource GlobalSettings_ServerUpdateOnServerStartLabel}" IsChecked="{Binding Config.ServerUpdate_OnServerStart, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ServerUpdateOnServerStartTooltip}" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="13" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_UpdateModSettingsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="5,0,0,5" IsChecked="{Binding Config.ServerUpdate_UpdateModsWhenUpdatingServer, Mode=TwoWay}" Content="{DynamicResource GlobalSettings_UpdateModWithServerLabel}" ToolTip="{DynamicResource GlobalSettings_UpdateModWithServerTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="3" Margin="0,0,0,5" IsChecked="{Binding Config.ServerUpdate_ForceUpdateMods, Mode=TwoWay}" Content="{DynamicResource GlobalSettings_ForceUpdateModsLabel}" ToolTip="{DynamicResource GlobalSettings_ForceUpdateModsTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="6" Margin="0,0,0,5" IsChecked="{Binding Config.ServerUpdate_ForceCopyMods, Mode=TwoWay}" Content="{DynamicResource GlobalSettings_ForceCopyModsLabel}" ToolTip="{DynamicResource GlobalSettings_ForceCopyModsTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="6" Margin="5,0,0,0" IsChecked="{Binding Config.ServerUpdate_ForceUpdateModsIfNoSteamInfo, Mode=TwoWay}" Content="{DynamicResource GlobalSettings_ForceUpdateModsIfNoSteamInfoLabel}" ToolTip="{DynamicResource GlobalSettings_ForceUpdateModsIfNoSteamInfoTooltip}" HorizontalAlignment="Left"/>
<cctl:AnnotatedSlider Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="4" Margin="0,0,5,0" Label="{DynamicResource GlobalSettings_WorkshopCacheExpiredHoursLabel}" Value="{Binding Config.WorkshopCache_ExpiredHours}" Minimum="0" Maximum="1000" SmallChange="1" LargeChange="12" TickFrequency="24" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Hours}" ToolTip="{DynamicResource GlobalSettings_WorkshopCacheExpiredHoursTooltip}"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="14" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}" IsEnabled="{Binding IsAdministrator}">
<GroupBox.Header>
<CheckBox IsChecked="{Binding Config.AutoBackup_EnableBackup}" Content="{DynamicResource GlobalSettings_AutoBackupLabel}" VerticalAlignment="Center"/>
</GroupBox.Header>
<Grid IsEnabled="{Binding Config.AutoBackup_EnableBackup}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource GlobalSettings_BackupIntervalLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="1" Width="100" HorizontalAlignment="Left" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_BackupIntervalTooltip}">
<TextBox.Text>
<Binding Path="Config.AutoBackup_BackupPeriod" Converter="{StaticResource MinutesToTimeValueConverter}"/>
</TextBox.Text>
</TextBox>
<CheckBox Grid.Row="0" Grid.Column="2" Margin="5,0,0,0" Content="{DynamicResource GlobalSettings_DeleteOldFilesLabel}" IsChecked="{Binding Config.AutoBackup_DeleteOldFiles, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_UseSmartCopyTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<cctl:AnnotatedSlider Grid.Row="0" Grid.Column="3" Grid.ColumnSpan="2" Margin="5,0,5,0" Label="{DynamicResource GlobalSettings_DeleteIntervalLabel}" Value="{Binding Config.AutoBackup_DeleteInterval}" Minimum="1" Maximum="1000" SmallChange="1" LargeChange="2" TickFrequency="5" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Days}" ToolTip="{DynamicResource GlobalSettings_DeleteIntervalTooltip}" IsEnabled="{Binding Config.AutoBackup_DeleteOldFiles}"/>
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource GlobalSettings_BackupWorldSaveLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="4" Margin="1" Text="{Binding Config.ServerBackup_WorldSaveMessage}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_BackupWorldSaveTooltip}"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="15" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}" IsEnabled="{Binding IsAdministrator}">
<GroupBox.Header>
<CheckBox IsChecked="{Binding Config.AutoUpdate_EnableUpdate}" Content="{DynamicResource GlobalSettings_AutoUpdateLabel}" VerticalAlignment="Center"/>
</GroupBox.Header>
<Grid IsEnabled="{Binding Config.AutoUpdate_EnableUpdate}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource GlobalSettings_CacheDirectoryLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="1" Grid.ColumnSpan="4" Text="{Binding Config.AutoUpdate_CacheDir}" IsReadOnly="True" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_CacheDirectoryTooltip}"/>
<Button Grid.Row="0" Grid.Column="5" Margin="5,1,0,1" Content="{StaticResource CacheDirectoryButtonContent}" VerticalAlignment="Center" Click="SetCacheDir_Click"/>
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource GlobalSettings_UpdateIntervalLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="1" Width="100" HorizontalAlignment="Left" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_UpdateIntervalTooltip}">
<TextBox.Text>
<Binding Path="Config.AutoUpdate_UpdatePeriod" Converter="{StaticResource MinutesToTimeValueConverter}"/>
</TextBox.Text>
</TextBox>
<CheckBox Grid.Row="1" Grid.Column="2" Margin="5,0,0,0" Content="{DynamicResource GlobalSettings_ValidateServerFilesLabel}" IsChecked="{Binding Config.AutoUpdate_ValidateServerFiles, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ValidateServerFilesTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="3" Margin="5,0,0,0" Content="{DynamicResource GlobalSettings_UseSmartCopyLabel}" IsChecked="{Binding Config.AutoUpdate_UseSmartCopy, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_UseSmartCopyTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="4" Margin="5,0,0,0" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_RetryOnFailLabel}" IsChecked="{Binding Config.AutoUpdate_RetryOnFail, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_RetryOnFailTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="2" Grid.Column="0" Margin="0,2,0,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_OverrideServerStartupLabel}" IsChecked="{Binding Config.AutoUpdate_OverrideServerStartup, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_OverrideServerStartupTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="3" Grid.Column="0" Margin="0,2,0,2" Grid.ColumnSpan="3" Content="{DynamicResource GlobalSettings_ParallelUpdateLabel}" IsChecked="{Binding Config.AutoUpdate_ParallelUpdate, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ParallelUpdateTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<cctl:AnnotatedSlider Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="4" Margin="0,2,0,2" Label="{DynamicResource GlobalSettings_SequencialDelayPeriodLabel}" Value="{Binding Config.AutoUpdate_SequencialDelayPeriod}" Minimum="0" Maximum="1200" SmallChange="1" LargeChange="2" TickFrequency="5" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Seconds}" ToolTip="{DynamicResource GlobalSettings_SequencialDelayPeriodTooltip}" IsEnabled="{Binding Config.AutoUpdate_ParallelUpdate, Converter={StaticResource InvertBooleanConverter}}"/>
<CheckBox Grid.Row="4" Grid.Column="0" Margin="0,2,20,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_ShowUpdateReasonLabel}" IsChecked="{Binding Config.AutoUpdate_ShowUpdateReason, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ShowUpdateReasonTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Label Grid.Row="4" Grid.Column="2" Margin="0,2,0,2" Content="{DynamicResource GlobalSettings_UpdateReasonPrefixLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="4" Grid.Column="3" Margin="1" Grid.ColumnSpan="2" IsEnabled="{Binding Config.AutoUpdate_ShowUpdateReason}" Text="{Binding Config.AutoUpdate_UpdateReasonPrefix}" MaxLength="50" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_UpdateReasonPrefixTooltip}"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="16" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_AutoRestartLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" Text="{DynamicResource GlobalSettings_RestartGraceIntervalInformationLabel}" TextWrapping="Wrap" VerticalAlignment="Center" FontWeight="Bold" Foreground="{DynamicResource WarningMessage}"/>
<CheckBox Grid.Row="1" Grid.Column="0" Margin="0,2,20,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_EnableRestartGraceIntervalLabel}" IsChecked="{Binding Config.AutoRestart_EnabledGracePeriod, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_EnableRestartGraceIntervalTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource GlobalSettings_RestartGraceIntervalLabel}" ToolTip="{DynamicResource GlobalSettings_RestartGraceIntervalTooltip}" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Margin="1" Width="100" HorizontalAlignment="Left" VerticalContentAlignment="Center" IsEnabled="{Binding Config.AutoRestart_EnabledGracePeriod}" ToolTip="{DynamicResource GlobalSettings_RestartGraceIntervalTooltip}">
<TextBox.Text>
<Binding Path="Config.AutoRestart_GracePeriod" Converter="{StaticResource MinutesToTimeValueConverter}"/>
</TextBox.Text>
</TextBox>
</Grid>
</GroupBox>
<GroupBox Grid.Row="17" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_ShutdownLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="100"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto" MinWidth="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Margin="5" Text="{DynamicResource GlobalSettings_ShutdownMessageInformationLabel}" TextWrapping="Wrap" VerticalAlignment="Center" FontWeight="Bold" Foreground="{DynamicResource WarningMessage}"/>
<CheckBox Grid.Row="1" Grid.Column="0" Margin="0,2,20,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_CheckForOnlinePlayersLabel}" IsChecked="{Binding Config.ServerShutdown_CheckForOnlinePlayers, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_CheckForOnlinePlayersTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="2" Margin="0,2,20,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_SendShutdownMessagesLabel}" IsChecked="{Binding Config.ServerShutdown_SendShutdownMessages, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_SendShutdownMessagesTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<cctl:AnnotatedSlider Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="1" Label="{DynamicResource GlobalSettings_ShutdownGraceIntervalLabel}" Value="{Binding Config.ServerShutdown_GracePeriod}" Minimum="0" Maximum="60" SmallChange="1" LargeChange="5" TickFrequency="1" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Minutes}" ToolTip="{DynamicResource GlobalSettings_ShutdownGraceIntervalTooltip}"/>
<Label Grid.Row="3" Grid.Column="0" Content="{DynamicResource GlobalSettings_ShutdownMessage1Label}" VerticalAlignment="Center"/>
<TextBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" Margin="1" Text="{Binding Config.ServerShutdown_GraceMessage1}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_ShutdownMessage1Tooltip}"/>
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource GlobalSettings_ShutdownMessage2Label}" VerticalAlignment="Center"/>
<TextBox Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="3" Margin="1" Text="{Binding Config.ServerShutdown_GraceMessage2}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_ShutdownMessage2Tooltip}"/>
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource GlobalSettings_ShutdownMessage3Label}" VerticalAlignment="Center"/>
<TextBox Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="3" Margin="1" Text="{Binding Config.ServerShutdown_GraceMessage3}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_ShutdownMessage3Tooltip}"/>
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource GlobalSettings_ShutdownWorldSaveLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="3" Margin="1" Text="{Binding Config.ServerShutdown_WorldSaveMessage}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_ShutdownWorldSaveTooltip}"/>
<Label Grid.Row="8" Grid.Column="0" Content="{DynamicResource GlobalSettings_ShutdownCancelLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="8" Grid.Column="1" Grid.ColumnSpan="3" Margin="1" Text="{Binding Config.ServerShutdown_CancelMessage}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_ShutdownCancelTooltip}"/>
<CheckBox Grid.Row="9" Grid.Column="0" Margin="0,2,20,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_ShutdownAllMessagesShowReasonLabel}" IsChecked="{Binding Config.ServerShutdown_AllMessagesShowReason, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_ShutdownAllMessagesShowReasonTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="18" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_AlertsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ServerStopMessageLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ServerStopMessage}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ServerStopMessageTooltip}"/>
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ServerShutdownMessageLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ServerShutdownMessage}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ServerShutdownMessageTooltip}"/>
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ServerStartedMessageLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ServerStartedMessage}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ServerStartedMessageTooltip}"/>
<CheckBox Grid.Row="3" Grid.Column="1" Margin="0,2,20,2" Grid.ColumnSpan="2" Content="{DynamicResource GlobalSettings_Alerts_ServerStartedMessageIncludeIPandPortLabel}" IsChecked="{Binding Config.Alert_ServerStartedMessageIncludeIPandPort, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_Alerts_ServerStartedMessageIncludeIPandPortTooltip}" VerticalAlignment="Center" HorizontalAlignment="Left"/>
<Label Grid.Row="4" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_BackupProcessErrorLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="4" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_BackupProcessError}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_BackupProcessErrorTooltip}"/>
<Label Grid.Row="5" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ShutdownProcessErrorLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="5" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ShutdownProcessError}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ShutdownProcessErrorTooltip}"/>
<Label Grid.Row="6" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_RestartProcessErrorLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="6" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_RestartProcessError}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_RestartProcessErrorTooltip}"/>
<Label Grid.Row="7" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_UpdateProcessErrorLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="7" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_UpdateProcessError}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_UpdateProcessErrorTooltip}"/>
<Label Grid.Row="8" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_UpdateResultsLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="8" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_UpdateResults}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_UpdateResultsTooltip}"/>
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ServerUpdateLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="9" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ServerUpdate}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ServerUpdateTooltip}"/>
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ServerStatusChangeLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="10" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ServerStatusChange}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ServerStatusChangeTooltip}"/>
<Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource GlobalSettings_Alerts_ModUpdateDetectedLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="11" Grid.Column="1" Margin="1" Text="{Binding Config.Alert_ModUpdateDetected}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_Alerts_ModUpdateDetectedTooltip}"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="19" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_EmailSettingsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource GlobalSettings_EmailHostLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="1" Text="{Binding Config.Email_Host}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailHostTooltip}"/>
<Label Grid.Row="0" Grid.Column="3" Content="{DynamicResource GlobalSettings_EmailPortLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="4" Margin="1" Text="{Binding Config.Email_Port}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailPortTooltip}"/>
<CheckBox Grid.Row="0" Grid.Column="7" Content="{DynamicResource GlobalSettings_EmailUseSSLLabel}" IsChecked="{Binding Config.Email_UseSSL}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailUseSSLTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="1" Content="{DynamicResource GlobalSettings_EmailUseDefaultCredentialsLabel}" IsChecked="{Binding Config.Email_UseDetaultCredentials}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailUseDefaultCredentialsTooltip}" HorizontalAlignment="Left"/>
<Label Grid.Row="1" Grid.Column="3" Content="{DynamicResource GlobalSettings_EmailUsernameLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="4" Margin="1" Text="{Binding Config.Email_Username}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailUsernameTooltip}" IsEnabled="{Binding Config.Email_UseDetaultCredentials, Converter={StaticResource InvertBooleanConverter}}"/>
<Label Grid.Row="1" Grid.Column="6" Content="{DynamicResource GlobalSettings_EmailPasswordLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="7" Margin="1" Name="HideEmailPasswordTextBox" Text="{DynamicResource ServerSettings_HidePasswordText}" IsEnabled="{Binding Config.Email_UseDetaultCredentials, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ServerSettings_HidePasswordTooltip}" GotFocus="HiddenField_GotFocus" Style="{StaticResource HiddenTextBoxStyle}"/>
<TextBox Grid.Row="1" Grid.Column="7" Margin="1" Name="EmailPasswordTextBox" Text="{Binding Config.Email_Password, Mode=TwoWay}" IsEnabled="{Binding Config.Email_UseDetaultCredentials, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource GlobalSettings_EmailPasswordTooltip}" LostFocus="HiddenField_LostFocus" VerticalContentAlignment="Center" Visibility="Collapsed"/>
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource GlobalSettings_EmailFromLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Margin="1" Text="{Binding Config.Email_From}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailFromTooltip}"/>
<Label Grid.Row="2" Grid.Column="3" Content="{DynamicResource GlobalSettings_EmailToLabel}" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="4" Grid.ColumnSpan="3" Margin="1" Text="{Binding Config.Email_To}" IsReadOnlyCaretVisible="True" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailToTooltip}"/>
<Button Grid.Row="2" Grid.Column="7" Margin="1" Padding="5,2,5,0" Content="{DynamicResource GlobalSettings_EmailTestButtonLabel}" Click="SendTestEmail_Click" HorizontalAlignment="Right"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="20" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_EmailNotifySettingsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_EmailNotify_AutoBackupLabel}" IsChecked="{Binding Config.EmailNotify_AutoBackup}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailNotify_AutoBackupTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="1" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_EmailNotify_AutoUpdateLabel}" IsChecked="{Binding Config.EmailNotify_AutoUpdate}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailNotify_AutoUpdateTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="2" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_EmailNotify_AutoRestartLabel}" IsChecked="{Binding Config.EmailNotify_AutoRestart}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailNotify_AutoRestartTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="3" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_EmailNotify_ShutdownRestartLabel}" IsChecked="{Binding Config.EmailNotify_ShutdownRestart}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_EmailNotify_ShutdownRestartTooltip}" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="21" Grid.Column="0" Grid.ColumnSpan="4" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<Label Content="{DynamicResource GlobalSettings_AdvancedSettingsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" Text="{DynamicResource GlobalSettings_AdvancedSettingsInformationLabel}" TextWrapping="Wrap" VerticalAlignment="Center" FontWeight="Bold" Foreground="{DynamicResource WarningMessage}"/>
<CheckBox Grid.Row="1" Grid.Column="0" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_UseShutdownCommandLabel}" IsChecked="{Binding Config.ServerShutdown_UseShutdownCommand}" VerticalAlignment="Center" VerticalContentAlignment="Center" ToolTip="{DynamicResource GlobalSettings_UseShutdownCommandTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_BackupWorldFileLabel}" IsChecked="{Binding Config.BackupWorldFile, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_BackupWorldFileTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="2" Grid.Column="0" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_CloseShutdownWindowWhenFinishedLabel}" IsChecked="{Binding Config.CloseShutdownWindowWhenFinished, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_CloseShutdownWindowWhenFinishedTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_VerifyServerAfterUpdateLabel}" IsChecked="{Binding Config.AutoUpdate_VerifyServerAfterUpdate, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_VerifyServerAfterUpdateTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="3" Grid.Column="0" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_SteamCmdRemoveQuitLabel}" IsChecked="{Binding CommonConfig.SteamCmdRemoveQuit, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_SteamCmdRemoveQuitTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="3" Grid.Column="1" Margin="5,0,5,5" Content="{DynamicResource GlobalSettings_UpdateDirectoryPermissionsLabel}" IsChecked="{Binding Config.UpdateDirectoryPermissions, Mode=TwoWay}" ToolTip="{DynamicResource GlobalSettings_UpdateDirectoryPermissionsTooltip}" HorizontalAlignment="Left"/>
<cctl:AnnotatedSlider Grid.Row="4" Grid.Column="0" Margin="1" Label="{DynamicResource GlobalSettings_WorldSaveDelayLabel}" Value="{Binding Config.ServerShutdown_WorldSaveDelay, Converter={cc:IntRangeValueConverter 10, 300}}" Minimum="10" Maximum="300" SmallChange="10" LargeChange="50" TickFrequency="1" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Seconds}" ToolTip="{DynamicResource GlobalSettings_WorldSaveDelayTooltip}"/>
</Grid>
</GroupBox>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View file

@ -0,0 +1,375 @@
using Microsoft.WindowsAPICodePack.Dialogs;
using NLog;
using ServerManagerTool.Common;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Xml;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for GlobalSettingsControl.xaml
/// </summary>
public partial class GlobalSettingsControl : UserControl
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty AppInstanceProperty = DependencyProperty.Register(nameof(AppInstance), typeof(App), typeof(GlobalSettingsControl), new PropertyMetadata(null));
public static readonly DependencyProperty IsAdministratorProperty = DependencyProperty.Register(nameof(IsAdministrator), typeof(bool), typeof(GlobalSettingsControl), new PropertyMetadata(false));
public GlobalSettingsControl()
{
this.AppInstance = App.Instance;
this.Config = Config.Default;
this.CommonConfig = CommonConfig.Default;
this.IsAdministrator = SecurityUtils.IsAdministrator();
this.Version = GetDeployedVersion();
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.DataContext = this;
}
public App AppInstance
{
get { return GetValue(AppInstanceProperty) as App; }
set { SetValue(AppInstanceProperty, value); }
}
public Config Config
{
get;
set;
}
public CommonConfig CommonConfig
{
get;
set;
}
public bool IsAdministrator
{
get { return (bool)GetValue(IsAdministratorProperty); }
set { SetValue(IsAdministratorProperty, value); }
}
public string Version
{
get;
set;
}
private string GetDeployedVersion()
{
XmlDocument xmlDoc = new XmlDocument();
Assembly asmCurrent = System.Reflection.Assembly.GetEntryAssembly();
string executePath = new Uri(asmCurrent.GetName().CodeBase).LocalPath;
xmlDoc.Load(executePath + ".manifest");
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("asmv1", "urn:schemas-microsoft-com:asm.v1");
string xPath = "/asmv1:assembly/asmv1:assemblyIdentity/@version";
XmlNode node = xmlDoc.SelectSingleNode(xPath, ns);
string version = node.Value;
return version;
}
private void Window_Closed(object sender, EventArgs e)
{
Window.GetWindow(this)?.Activate();
}
private void ApplySteamAPIKey_Click(object sender, RoutedEventArgs e)
{
Process.Start(CommonConfig.Default.SteamAPIKeyUrl);
}
private async void SendTestEmail_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await Task.Run(() =>
{
var email = new EmailUtil()
{
EnableSsl = Config.Default.Email_UseSSL,
MailServer = Config.Default.Email_Host,
Port = Config.Default.Email_Port,
UseDefaultCredentials = Config.Default.Email_UseDetaultCredentials,
Credentials = Config.Default.Email_UseDetaultCredentials ? null : new System.Net.NetworkCredential(Config.Default.Email_Username, Config.Default.Email_Password),
};
email.SendEmail(Config.Default.Email_From, Config.Default.Email_To?.Split(','), "Server Manager Test Email", "This is a test email sent from the server manager settings window.", true);
});
MessageBox.Show("Test email sent.", "Send Email Confirmation", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
var message = ex.Message;
if (ex.InnerException != null)
message += $"\r\n{ex.InnerException.Message}";
MessageBox.Show(message, "Send Email Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
public void SetDataDir_Click(object sender, RoutedEventArgs args)
{
var optionResult = MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_DataDirectoryChange_ConfirmLabel"), _globalizer.GetResourceString("GlobalSettings_DataDirectoryChange_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (optionResult == MessageBoxResult.Yes)
{
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
dialog.Title = _globalizer.GetResourceString("Application_DataDirectoryTitle");
dialog.InitialDirectory = Config.Default.DataPath;
var result = dialog.ShowDialog(Window.GetWindow(this));
if (result == CommonFileDialogResult.Ok)
{
if (!String.Equals(dialog.FileName, Config.Default.DataPath))
{
try
{
// Set up the destination directories
string newConfigDirectory = Path.Combine(dialog.FileName, Config.Default.ProfilesRelativePath);
string oldSteamDirectory = Path.Combine(Config.Default.DataPath, CommonConfig.Default.SteamCmdRelativePath);
string newSteamDirectory = Path.Combine(dialog.FileName, CommonConfig.Default.SteamCmdRelativePath);
Directory.CreateDirectory(newConfigDirectory);
Directory.CreateDirectory(newSteamDirectory);
// Copy the Profiles
foreach (var file in Directory.EnumerateFiles(Config.Default.ConfigPath, "*.*", SearchOption.AllDirectories))
{
string sourceWithoutRoot = file.Substring(Config.Default.ConfigPath.Length + 1);
string destination = Path.Combine(newConfigDirectory, sourceWithoutRoot);
if (!File.Exists(destination))
{
Directory.CreateDirectory(Path.GetDirectoryName(destination));
File.Copy(file, destination);
}
}
// Copy the SteamCMD files
foreach (var file in Directory.EnumerateFiles(oldSteamDirectory, "*.*", SearchOption.AllDirectories))
{
string sourceWithoutRoot = file.Substring(oldSteamDirectory.Length + 1);
string destination = Path.Combine(newSteamDirectory, sourceWithoutRoot);
if (!File.Exists(destination))
{
Directory.CreateDirectory(Path.GetDirectoryName(destination));
File.Copy(file, destination);
}
}
// Remove the old directories
Directory.Delete(Config.Default.ConfigPath, true);
Directory.Delete(oldSteamDirectory, true);
// Update the config
Config.Default.DataPath = dialog.FileName;
Config.Default.ConfigPath = newConfigDirectory;
App.ReconfigureLogging();
}
catch (Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("GlobalSettings_DataDirectoryChange_FailedLabel"), ex.Message), _globalizer.GetResourceString("GlobalSettings_DataDirectoryChange_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
}
}
}
private void SetBackupDir_Click(object sender, RoutedEventArgs e)
{
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
dialog.Title = _globalizer.GetResourceString("GlobalSettings_DataDirectoryTitle");
dialog.InitialDirectory = Config.Default.BackupPath;
var result = dialog.ShowDialog(Window.GetWindow(this));
if (result == CommonFileDialogResult.Ok)
{
if (!String.Equals(dialog.FileName, Config.Default.BackupPath))
{
Config.Default.BackupPath = dialog.FileName;
}
}
}
private void ClearBackupDir_Click(object sender, RoutedEventArgs e)
{
Config.Default.BackupPath = string.Empty;
}
private void SetCacheDir_Click(object sender, RoutedEventArgs e)
{
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
dialog.Title = _globalizer.GetResourceString("GlobalSettings_CacheDirectoryTitle");
dialog.InitialDirectory = Config.Default.DataPath;
var result = dialog.ShowDialog(Window.GetWindow(this));
if (result == CommonFileDialogResult.Ok)
{
if (!String.Equals(dialog.FileName, Config.Default.AutoUpdate_CacheDir))
{
Config.Default.AutoUpdate_CacheDir = dialog.FileName;
}
}
}
private void SteamAPIKeyHelp_Click(object sender, RoutedEventArgs e)
{
Process.Start(Config.Default.SteamWebAPIKeyHelpUrl);
}
private async void SteamCMDAuthenticate_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
if (string.IsNullOrWhiteSpace(Config.Default.SteamCmd_Username))
{
MessageBox.Show("A steam username has not be entered.", "SteamCMD Authentication Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var steamCmdFile = SteamCmdUpdater.GetSteamCmdFile(Config.Default.DataPath);
if (string.IsNullOrWhiteSpace(steamCmdFile) || !File.Exists(steamCmdFile))
{
MessageBox.Show("Could not locate the SteamCMD executable. Try reinstalling SteamCMD.", "SteamCMD Authentication Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
var steamCmdArgs = SteamUtils.BuildSteamCmdArguments(CommonConfig.Default.SteamCmdRemoveQuit, CommonConfig.Default.SteamCmdAuthenticateArgs, Config.Default.SteamCmd_Username, Config.Default.SteamCmd_Password);
var workingDirectory = Config.Default.DataPath;
var result = await ProcessUtils.RunProcessAsync(steamCmdFile, steamCmdArgs, string.Empty, workingDirectory, null, null, null, CancellationToken.None);
if (result)
MessageBox.Show("The authentication was completed.", "SteamCMD Authentication", MessageBoxButton.OK, MessageBoxImage.Information);
else
MessageBox.Show("An error occurred while trying to authenticate with steam. Please try again.", "SteamCMD Authentication Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "SteamCMD Authentication Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private void LanguageSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Config.CultureName = AvailableLanguages.Instance.SelectedLanguage;
}
private void StyleSelectionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Config.StyleName = AvailableStyles.Instance.SelectedStyle;
}
private void HiddenField_GotFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox hideTextBox)
{
TextBox textBox = null;
if (Equals(hideTextBox, HideSteamPasswordTextBox))
textBox = SteamPasswordTextBox;
if (Equals(hideTextBox, HideSteamAPIKeyTextBox))
textBox = SteamAPIKeyTextBox;
if (Equals(hideTextBox, HideEmailPasswordTextBox))
textBox = EmailPasswordTextBox;
if (textBox != null)
{
textBox.Visibility = System.Windows.Visibility.Visible;
hideTextBox.Visibility = System.Windows.Visibility.Collapsed;
textBox.Focus();
}
UpdateLayout();
}
}
private void HiddenField_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox textBox)
{
TextBox hideTextBox = null;
if (textBox == SteamPasswordTextBox)
hideTextBox = HideSteamPasswordTextBox;
if (textBox == SteamAPIKeyTextBox)
hideTextBox = HideSteamAPIKeyTextBox;
if (textBox == EmailPasswordTextBox)
hideTextBox = HideEmailPasswordTextBox;
if (hideTextBox != null)
{
hideTextBox.Visibility = System.Windows.Visibility.Visible;
textBox.Visibility = System.Windows.Visibility.Collapsed;
}
UpdateLayout();
}
}
private void ResetSettings_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_ResetSettings_ConfirmLabel"), _globalizer.GetResourceString("GlobalSettings_ResetSettings_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
int exitCode = 0;
try
{
Config.Default.Reset();
Config.Default.UpgradeConfig = false;
Config.Default.Save();
Config.Default.Reload();
CommonConfig.Default.Reset();
CommonConfig.Default.UpgradeConfig = false;
CommonConfig.Default.Save();
CommonConfig.Default.Reload();
}
catch (Exception ex)
{
Logger.Error("Exception while resettiing the settings: {0}\n{1}", ex.Message, ex.StackTrace);
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_ResetSettings_FailedLabel"), _globalizer.GetResourceString("GlobalSettings_ResetSettings_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
exitCode = 1;
}
finally
{
App.Current.Shutdown(exitCode);
}
}
}
}

View file

@ -0,0 +1,100 @@
<Window x:Class="ServerManagerTool.GuildProfileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
Width="600" ResizeMode="NoResize" SizeToContent="Height" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Icon="../Art/favicon.ico" Title="{Binding WindowTitle}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:UnixTimeToDateTimeConverter x:Key="UnixTimeToDateTimeConverter"/>
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
</Style>
<Style x:Key="OnlineListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsOnline}" Value="True">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Margin="3" CanVerticallyScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Profile_NameLabel}" VerticalAlignment="Center"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding Player.GuildName}" VerticalAlignment="Center" FontWeight="Bold" FontSize="13.333"/>
</Grid>
<GroupBox HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{DynamicResource Profile_TribeSectionLabel}" VerticalAlignment="Center" FontWeight="Bold" FontSize="13.333"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource Profile_IdLabel}"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding GuildData.GuildId}"/>
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Profile_TribeOwnerLabel}"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding GuildOwner}"/>
</Grid>
</GroupBox>
<GroupBox HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{DynamicResource Profile_TribeMembersSectionLabel}" VerticalAlignment="Center" FontWeight="Bold" FontSize="13.333"/>
</GroupBox.Header>
<Grid>
<ListView x:Name="GuildMembersListView" ItemsSource="{Binding GuildPlayers}" Height="200" HorizontalContentAlignment="Stretch" ItemContainerStyle="{DynamicResource OnlineListViewItemStyle}">
<ListView.View>
<GridView>
<GridViewColumn Header="{DynamicResource Profile_CharacterNameColumnLabel}" Width="Auto" DisplayMemberBinding="{Binding PlayerData.CharacterName}"/>
<GridViewColumn Header="{DynamicResource Profile_LevelColumnLabel}" Width="Auto" DisplayMemberBinding="{Binding PlayerData.Level}"/>
<GridViewColumn Header="{DynamicResource Profile_OnlineColumnLabel}" Width="Auto" DisplayMemberBinding="{Binding IsOnline}"/>
<GridViewColumn Header="{DynamicResource Profile_LastOnlineColumnLabel}" Width="Auto" DisplayMemberBinding="{Binding LastOnline, Converter={StaticResource UnixTimeToDateTimeConverter}}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</GroupBox>
</StackPanel>
</Grid>
</Window>

View file

@ -0,0 +1,91 @@
using ConanData;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Lib.ViewModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for GuildProfileWindow.xaml
/// </summary>
public partial class GuildProfileWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public GuildProfileWindow(PlayerInfo player, ICollection<PlayerInfo> players, String serverFolder)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.Player = player;
this.Players = players;
this.ServerFolder = serverFolder;
this.DataContext = this;
}
public PlayerInfo Player
{
get;
private set;
}
public ICollection<PlayerInfo> Players
{
get;
private set;
}
public String ServerFolder
{
get;
private set;
}
public PlayerData PlayerData => Player?.PlayerData;
public GuildData GuildData => Player?.PlayerData?.Guild;
public String GuildOwner => GuildData != null && GuildData.Owner != null ? $"{GuildData.Owner.CharacterName}" : null;
public ICollection<PlayerInfo> GuildPlayers
{
get
{
if (GuildData == null) return null;
ICollection<PlayerInfo> players = new List<PlayerInfo>();
foreach (var guildPlayer in GuildData.Players)
{
var player = Players.FirstOrDefault(p => p.PlayerId.ToString() == guildPlayer.PlayerId);
if (player != null)
players.Add(player);
}
return players;
}
}
public String WindowTitle => String.Format(_globalizer.GetResourceString("Profile_WindowTitle_Tribe"), Player.GuildName);
public ICommand ExplorerLinkCommand
{
get
{
return new RelayCommand<String>(
execute: (action) =>
{
if (String.IsNullOrWhiteSpace(action)) return;
Process.Start("explorer.exe", action);
},
canExecute: (action) => true
);
}
}
}
}

View file

@ -0,0 +1,521 @@
<Window x:Class="ServerManagerTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:eo="http://schemas.essentialobjects.com/wpf/"
xmlns:sm="clr-namespace:ServerManagerTool"
xmlns:enum="clr-namespace:ServerManagerTool.Enums"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:cvr="clr-namespace:ServerManagerTool.Common.ValidationRules;assembly=ServerManager.Common"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:tsk="clr-namespace:Microsoft.Win32.TaskScheduler;assembly=Microsoft.Win32.TaskScheduler"
xmlns:tb="http://www.hardcodet.net/taskbar"
MinWidth="900" MinHeight="600" Width="1100" Height="900" Left="50" Top="50"
Loaded="Window_Loaded" SizeChanged="Window_SizeChanged" StateChanged="Window_StateChanged"
Name="Main" Icon="../Art/favicon.ico" Title="{DynamicResource MainWindow_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<cc:HasStringValueConverter x:Key="HasStringValueConverter"/>
<Style x:Key="ButtonStyle1" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="#00ffffff"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Width" Value="22"/>
<Setter Property="Height" Value="22"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="0"/>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Width" Value="22"/>
<Setter Property="Height" Value="22"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="0"/>
</Style>
<Style x:Key="ButtonStyle3" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="#00ffffff"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Height" Value="22"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="0"/>
</Style>
<Style x:Key="ButtonStyle5" TargetType="Button" BasedOn="{StaticResource ButtonStyle1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="15" Background="{TemplateBinding Background}" BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<DockPanel x:Name="dockPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" LastChildFill="False">
<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="Background" Value="#4F4F4F"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AppInstance.BetaVersion}" Value="True">
<Setter Property="Background" Value="#378CFB"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<Grid DockPanel.Dock="Left">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Background="Transparent" Foreground="White" FontSize="20" FontWeight="Bold" Margin="5,0,5,0" Content="{DynamicResource MainWindow_Label}" BorderThickness="0"/>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Label Margin="5,0,0,0" Background="Transparent" Foreground="White" FontSize="12" HorizontalAlignment="Right" VerticalAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Content" Value="{DynamicResource MainWindow_VersionLabel}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AppInstance.BetaVersion}" Value="True">
<Setter Property="Content" Value="{DynamicResource MainWindow_BetaVersionLabel}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Label Margin="5,0,0,0" Background="Transparent" Foreground="White" FontSize="12" Content="{Binding Source={x:Static sm:App.Instance}, Path=Version}" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<Button Margin="5" Click="PatchNotes_Click" ToolTip="{DynamicResource ServerSettings_PatchNotesTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/ChangeNotes.ico,Size=32}"/>
</Button>
<Button Margin="5,0,0,0" Background="#00AA00" Foreground="White" Padding="1" BorderThickness="1" BorderBrush="White" ContentStringFormat="{DynamicResource MainWindow_UpdateToLabelFormat}" Content="{Binding LatestServerManagerVersion}" Click="Upgrade_Click" VerticalAlignment="Center" >
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding NewServerManagerAvailable}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding NewServerManagerAvailable}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
<Grid DockPanel.Dock="Right" VerticalAlignment="Top" Margin="5" MinWidth="130">
<Grid.RowDefinitions>
<RowDefinition MinHeight="22"/>
<RowDefinition MinHeight="22"/>
<RowDefinition MinHeight="22"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="25"/>
<ColumnDefinition MinWidth="25"/>
<ColumnDefinition MinWidth="25"/>
<ColumnDefinition MinWidth="25"/>
<ColumnDefinition MinWidth="25"/>
<ColumnDefinition MinWidth="25"/>
<ColumnDefinition MinWidth="25"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="4" Margin="5,2,0,0" Grid.ColumnSpan="3" Click="Donate_Click" ToolTip="{DynamicResource MainWindow_DonateTooltip}" Style="{StaticResource ButtonStyle3}">
<Image Source="../Art/Donate.png"/>
</Button>
<Button Grid.Row="2" Grid.Column="0" Margin="5,0,0,0" Click="ServerMonitor_Click" ToolTip="{DynamicResource MainWindow_OpenServerMonitorTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Servers.ico,Size=32}"/>
</Button>
<Button Grid.Row="2" Grid.Column="1" Margin="5,0,0,0" HorizontalAlignment="Right" Click="GameData_Click" ToolTip="{DynamicResource MainWindow_OpenGameDataTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Document.ico,Size=32}"/>
</Button>
<Button Grid.Row="2" Grid.Column="2" Margin="5,0,0,0" Click="Plugins_Click" ToolTip="{DynamicResource MainWindow_OpenPluginsTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Plugin.ico,Size=32}"/>
</Button>
<Button Grid.Row="2" Grid.Column="3" Margin="5,0,0,0" Click="OpenLogFolder_Click" ToolTip="{DynamicResource MainWindow_OpenLogFolderTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Logs.ico,Size=32}"/>
</Button>
<Button Grid.Row="2" Grid.Column="4" Margin="5,0,0,0" Click="SteamCMD_Click" ToolTip="{DynamicResource MainWindow_SteamCMDTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Steam.ico,Size=32}"/>
</Button>
<Button Grid.Row="2" Grid.Column="5" Margin="5,0,0,0" Click="Settings_Click" ToolTip="{DynamicResource MainWindow_OpenSettingsTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Settings.ico,Size=32}"/>
</Button>
<Button Grid.Row="2" Grid.Column="6" Margin="5,0,0,0" Click="Help_Click" ToolTip="{DynamicResource MainWindow_OpenHelpTooltip}" Style="{StaticResource ButtonStyle1}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Help.ico,Size=32}"/>
</Button>
</Grid>
<Grid DockPanel.Dock="Right" VerticalAlignment="Top" Margin="10,0,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Margin="0,-2,0,-3" Background="Transparent" Foreground="White" FontSize="11" Content="{DynamicResource MainWindow_TaskStatusLabel}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" Margin="5,0,0,0">
<Label Margin="0" Background="Transparent" Foreground="White" FontSize="10" Content="{DynamicResource MainWindow_AutoBackupTaskLabel}" VerticalAlignment="Center" HorizontalAlignment="Left" ToolTip="{Binding AutoBackupNextRunTime}" ToolTipService.IsEnabled="{Binding AutoBackupNextRunTime, Converter={StaticResource HasStringValueConverter}}"/>
<Label Margin="0" Background="Transparent" FontSize="10" Content="{Binding AutoBackupStateString}" VerticalAlignment="Center" HorizontalAlignment="Left" ToolTip="{Binding AutoBackupNextRunTime}" ToolTipService.IsEnabled="{Binding AutoBackupNextRunTime, Converter={StaticResource HasStringValueConverter}}">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="White"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AutoBackupState}" Value="{x:Static tsk:TaskState.Disabled}">
<Setter Property="Foreground" Value="DarkRed"/>
</DataTrigger>
<DataTrigger Binding="{Binding AutoBackupState}" Value="{x:Static tsk:TaskState.Ready}">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding AutoBackupState}" Value="{x:Static tsk:TaskState.Running}">
<Setter Property="Foreground" Value="LightGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button Margin="5,0,0,0" Background="#00AA00" Foreground="White" Padding="1" Content="{DynamicResource MainWindow_AutoBackupTaskRunLabel}" BorderThickness="1" BorderBrush="White" Click="AutoBackupTaskRun_Click" VerticalAlignment="Center" ToolTip="{DynamicResource MainWindow_AutoBackupTaskRunTooltip}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AutoBackupState}" Value="{x:Static tsk:TaskState.Ready}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Margin="5,0,0,0" Background="#AA8A00" Foreground="White" Padding="1" BorderThickness="1" BorderBrush="White" Click="AutoBackupTaskState_Click" VerticalAlignment="Center">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="{DynamicResource MainWindow_AutoBackupTaskEnableLabel}"/>
<Setter Property="ToolTip" Value="{DynamicResource MainWindow_AutoBackupTaskEnableTooltip}"/>
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AutoBackupState}" Value="{x:Static tsk:TaskState.Disabled}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Content" Value="{DynamicResource MainWindow_AutoBackupTaskEnableLabel}"/>
<Setter Property="ToolTip" Value="{DynamicResource MainWindow_AutoBackupTaskEnableTooltip}"/>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AutoBackupState}" Value="{x:Static tsk:TaskState.Ready}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Content" Value="{DynamicResource MainWindow_AutoBackupTaskDisableLabel}"/>
<Setter Property="ToolTip" Value="{DynamicResource MainWindow_AutoBackupTaskDisableTooltip}"/>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Center" Margin="5,0,0,0">
<Label Margin="0" Background="Transparent" Foreground="White" FontSize="10" Content="{DynamicResource MainWindow_AutoUpdateTaskLabel}" VerticalAlignment="Center" HorizontalAlignment="Left" ToolTip="{Binding AutoUpdateNextRunTime}" ToolTipService.IsEnabled="{Binding AutoUpdateNextRunTime, Converter={StaticResource HasStringValueConverter}}"/>
<Label Margin="0" Background="Transparent" FontSize="10" Content="{Binding AutoUpdateStateString}" VerticalAlignment="Center" HorizontalAlignment="Left" ToolTip="{Binding AutoUpdateNextRunTime}" ToolTipService.IsEnabled="{Binding AutoUpdateNextRunTime, Converter={StaticResource HasStringValueConverter}}">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="White"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AutoUpdateState}" Value="{x:Static tsk:TaskState.Disabled}">
<Setter Property="Foreground" Value="DarkRed"/>
</DataTrigger>
<DataTrigger Binding="{Binding AutoUpdateState}" Value="{x:Static tsk:TaskState.Ready}">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding AutoUpdateState}" Value="{x:Static tsk:TaskState.Running}">
<Setter Property="Foreground" Value="LightGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button Margin="5,0,0,0" Background="#00AA00" Foreground="White" Padding="1" Content="{DynamicResource MainWindow_AutoUpdateTaskRunLabel}" BorderThickness="1" BorderBrush="White" Click="AutoUpdateTaskRun_Click" VerticalAlignment="Center" ToolTip="{DynamicResource MainWindow_AutoUpdateTaskRunTooltip}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AutoUpdateState}" Value="{x:Static tsk:TaskState.Ready}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Margin="5,0,0,0" Background="#AA8A00" Foreground="White" Padding="1" BorderThickness="1" BorderBrush="White" Click="AutoUpdateTaskState_Click" VerticalAlignment="Center">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Content" Value="{DynamicResource MainWindow_AutoUpdateTaskEnableLabel}"/>
<Setter Property="ToolTip" Value="{DynamicResource MainWindow_AutoUpdateTaskEnableTooltip}"/>
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AutoUpdateState}" Value="{x:Static tsk:TaskState.Disabled}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Content" Value="{DynamicResource MainWindow_AutoUpdateTaskEnableLabel}"/>
<Setter Property="ToolTip" Value="{DynamicResource MainWindow_AutoUpdateTaskEnableTooltip}"/>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding AutoUpdateState}" Value="{x:Static tsk:TaskState.Ready}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Content" Value="{DynamicResource MainWindow_AutoUpdateTaskDisableLabel}"/>
<Setter Property="ToolTip" Value="{DynamicResource MainWindow_AutoUpdateTaskDisableTooltip}"/>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
<Grid DockPanel.Dock="Top" VerticalAlignment="Top" Margin="10,0,10,0" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Margin="0" Background="Transparent" Foreground="White" FontSize="11" Content="{DynamicResource MainWindow_MyIpLabel}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" Margin="5,0,0,0">
<TextBox FontSize="12" Height="22" Width="120" VerticalAlignment="Bottom" Margin="0" VerticalContentAlignment="Center" ToolTip="{DynamicResource MainWindow_MyIpTooltip}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<!-- Placeholder for the TextBox itself -->
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{DynamicResource MainWindow_MyIpError}" Background="Red" Foreground="White"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
<TextBox.Text>
<Binding Path="Config.MachinePublicIP" ElementName="Main">
<Binding.ValidationRules>
<cvr:IpValidationRule ValidatesOnTargetUpdated="true" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Button Grid.Row="1" Margin="5,0,0,0" Click="RefreshPublicIP_Click" ToolTip="{DynamicResource MainWindow_RefreshMyIpTooltip}" Style="{StaticResource ButtonStyle2}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Reload.ico,Size=32}"/>
</Button>
</StackPanel>
</Grid>
</DockPanel>
<eo:TabControl Name="Tabs" Grid.Row ="1" Margin="0,5,0,0" ShowCloseTabButton="True" ShowNewTabButton="True" NewItemRequested="Servers_AddNew" PreviewItemClose="Servers_Remove" ItemsSource="{Binding ServerManager.Servers}">
<eo:TabControl.HeaderTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Button Grid.Row="0" Grid.Column="4" Margin="5,0,0,0" Command="{Binding StatusButtonCommand, ElementName=Main}" CommandParameter="{Binding}">
<Button.Style>
<Style BasedOn="{StaticResource ButtonStyle5}" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUnknownLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusInitializingLabel}"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}"/>
<Condition Binding="{Binding Config.ServerStatus_EnableActions, ElementName=Main}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusRunningLabel}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}"/>
<Condition Binding="{Binding Config.ServerStatus_EnableActions, ElementName=Main}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusRunningLabel}"/>
</MultiDataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusStoppingLabel}"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}"/>
<Condition Binding="{Binding Config.ServerStatus_EnableActions, ElementName=Main}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusStoppedLabel}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}"/>
<Condition Binding="{Binding Config.ServerStatus_EnableActions, ElementName=Main}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusStoppedLabel}"/>
</MultiDataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUnknownLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUpdatingLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUninstalledLabel}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Image Margin="0,2" VerticalAlignment="Center" Width="16" Height="16">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusUnknown.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUnknownLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusStarting.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusInitializingLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusOn.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusRunningLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusOn.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusStoppingLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusOff.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusStoppedLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusUnknown.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUnknownLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUpdatingLabel}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/StatusUnknown.ico,Size=32}"/>
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_RuntimeStatusUninstalledLabel}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
<TextBlock Margin="2" VerticalAlignment="Center" Text="{Binding Profile.ProfileName}" ToolTip="{Binding Profile.ProfileToolTip}"/>
</StackPanel>
</DataTemplate>
</eo:TabControl.HeaderTemplate>
<eo:TabControl.NewTabButtonStyle>
<Style TargetType="eo:BareButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="#8C8E94" BorderThickness="1,1,1,0" Padding="3,0">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="#F3F3F3" />
<GradientStop Offset="0.5" Color="#EBEBEB" />
<GradientStop Offset="0.5" Color="#DDDDDD" />
<GradientStop Offset="1" Color="#CDCDCD" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Image Margin="0,2" VerticalAlignment="Center" Source="{com:Icon Path=/ConanServerManager;component/Art/Add.ico,Size=32}" Width="16" Height="16" ToolTip="{DynamicResource MainWindow_NewProfileTooltip}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</eo:TabControl.NewTabButtonStyle>
<eo:TabControl.CloseTabButtonStyle>
<Style TargetType="eo:BareButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Margin="0,2" VerticalAlignment="Center" Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}" Width="16" Height="16" ToolTip="{DynamicResource MainWindow_CloseProfileTooltip}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</eo:TabControl.CloseTabButtonStyle>
<eo:TabControl.ContentTemplate>
<DataTemplate>
<sm:ServerSettingsControl Server="{Binding}"/>
</DataTemplate>
</eo:TabControl.ContentTemplate>
</eo:TabControl>
</Grid>
</DockPanel>
<!-- 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 Background="Black" Opacity="0.5"/>
<Border MinWidth="250" Background="Orange" BorderBrush="Black" BorderThickness="1" CornerRadius="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<Label x:Name="OverlayMessage" Margin="5" FontWeight="Bold" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Grid>
<tb:TaskbarIcon
Visibility="Visible"
ToolTipText="{Binding Title}"
IconSource="../Art/favicon.ico"
LeftClickCommand="{Binding ShowWindowCommand, ElementName=Main}"
MenuActivation="RightClick">
<tb:TaskbarIcon.Resources>
<clib:BindingProxy x:Key="proxy" Data="{Binding ElementName=Main}"/>
</tb:TaskbarIcon.Resources>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Application_NotifyIcon_ShowMainWindow}" Command="{Binding Source={StaticResource proxy}, Path=Data.ShowWindowCommand}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</Grid>
</Window>

View file

@ -0,0 +1,763 @@
using EO.Wpf;
using NLog;
using ServerManagerTool.Common;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common;
using ServerManagerTool.Windows;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private readonly ActionQueue versionChecker;
private readonly ActionQueue scheduledTaskChecker;
public static readonly DependencyProperty AppInstanceProperty = DependencyProperty.Register(nameof(AppInstance), typeof(App), typeof(MainWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ConfigProperty = DependencyProperty.Register(nameof(Config), typeof(Config), typeof(MainWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ServerManagerProperty = DependencyProperty.Register(nameof(ServerManager), typeof(ServerManager), typeof(MainWindow), new PropertyMetadata(null));
public static readonly DependencyProperty AutoBackupStateProperty = DependencyProperty.Register(nameof(AutoBackupState), typeof(Microsoft.Win32.TaskScheduler.TaskState), typeof(MainWindow), new PropertyMetadata(Microsoft.Win32.TaskScheduler.TaskState.Unknown));
public static readonly DependencyProperty AutoBackupStateStringProperty = DependencyProperty.Register(nameof(AutoBackupStateString), typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty AutoBackupNextRunTimeProperty = DependencyProperty.Register(nameof(AutoBackupNextRunTime), typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty AutoUpdateStateProperty = DependencyProperty.Register(nameof(AutoUpdateState), typeof(Microsoft.Win32.TaskScheduler.TaskState), typeof(MainWindow), new PropertyMetadata(Microsoft.Win32.TaskScheduler.TaskState.Unknown));
public static readonly DependencyProperty AutoUpdateStateStringProperty = DependencyProperty.Register(nameof(AutoUpdateStateString), typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty AutoUpdateNextRunTimeProperty = DependencyProperty.Register(nameof(AutoUpdateNextRunTime), typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsIpValidProperty = DependencyProperty.Register(nameof(IsIpValid), typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
public static readonly DependencyProperty LatestServerManagerVersionProperty = DependencyProperty.Register(nameof(LatestServerManagerVersion), typeof(Version), typeof(MainWindow), new PropertyMetadata(new Version()));
public static readonly DependencyProperty NewServerManagerAvailableProperty = DependencyProperty.Register(nameof(NewServerManagerAvailable), typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
public App AppInstance
{
get { return GetValue(AppInstanceProperty) as App; }
set { SetValue(AppInstanceProperty, value); }
}
public Config Config
{
get { return GetValue(ConfigProperty) as Config; }
set { SetValue(ConfigProperty, value); }
}
public ServerManager ServerManager
{
get { return (ServerManager)GetValue(ServerManagerProperty); }
set { SetValue(ServerManagerProperty, value); }
}
public Microsoft.Win32.TaskScheduler.TaskState AutoBackupState
{
get { return (Microsoft.Win32.TaskScheduler.TaskState)GetValue(AutoBackupStateProperty); }
set { SetValue(AutoBackupStateProperty, value); }
}
public string AutoBackupStateString
{
get { return (string)GetValue(AutoBackupStateStringProperty); }
set { SetValue(AutoBackupStateStringProperty, value); }
}
public string AutoBackupNextRunTime
{
get { return (string)GetValue(AutoBackupNextRunTimeProperty); }
set { SetValue(AutoBackupNextRunTimeProperty, value); }
}
public Microsoft.Win32.TaskScheduler.TaskState AutoUpdateState
{
get { return (Microsoft.Win32.TaskScheduler.TaskState)GetValue(AutoUpdateStateProperty); }
set { SetValue(AutoUpdateStateProperty, value); }
}
public string AutoUpdateStateString
{
get { return (string)GetValue(AutoUpdateStateStringProperty); }
set { SetValue(AutoUpdateStateStringProperty, value); }
}
public string AutoUpdateNextRunTime
{
get { return (string)GetValue(AutoUpdateNextRunTimeProperty); }
set { SetValue(AutoUpdateNextRunTimeProperty, value); }
}
public bool IsAdministrator
{
get;
set;
}
public bool IsIpValid
{
get { return (bool)GetValue(IsIpValidProperty); }
set { SetValue(IsIpValidProperty, value); }
}
public Version LatestServerManagerVersion
{
get { return (Version)GetValue(LatestServerManagerVersionProperty); }
set { SetValue(LatestServerManagerVersionProperty, value); }
}
public bool NewServerManagerAvailable
{
get { return (bool)GetValue(NewServerManagerAvailableProperty); }
set { SetValue(NewServerManagerAvailableProperty, value); }
}
public MainWindow()
{
this.AppInstance = App.Instance;
this.Config = Config.Default;
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.ServerManager = ServerManager.Instance;
this.DataContext = this;
this.versionChecker = new ActionQueue();
this.scheduledTaskChecker = new ActionQueue();
IsAdministrator = SecurityUtils.IsAdministrator();
if (!string.IsNullOrWhiteSpace(App.Instance.Title))
{
this.Title = App.Instance.Title;
}
else
{
if (IsAdministrator)
{
this.Title = _globalizer.GetResourceString("MainWindow_TitleWithAdmin");
}
else
{
this.Title = _globalizer.GetResourceString("MainWindow_Title");
}
}
this.Height = Config.Default.MainWindow_Height;
this.Width = Config.Default.MainWindow_Width;
// hook into the language change event
GlobalizedApplication.Instance.GlobalizationManager.ResourceDictionaryChangedEvent += ResourceDictionaryChangedEvent;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//
// Kick off the initialization.
//
TaskUtils.RunOnUIThreadAsync(() =>
{
// We need to load the set of existing servers, or create a blank one if we don't have any...
foreach (var profile in Directory.EnumerateFiles(Config.Default.ConfigPath, "*" + Config.Default.ProfileExtension))
{
try
{
ServerManager.Instance.AddFromPath(profile);
}
catch (Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("MainWindow_ProfileLoad_FailedLabel"), profile, ex.Message, ex.StackTrace), _globalizer.GetResourceString("MainWindow_ProfileLoad_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
ServerManager.Instance.SortServers();
ServerManager.Instance.CheckProfiles();
Tabs.SelectedIndex = 0;
}).DoNotWait();
this.versionChecker.PostAction(CheckForUpdates).DoNotWait();
this.scheduledTaskChecker.PostAction(CheckForScheduledTasks).DoNotWait();
}
private void Window_Closed(object sender, EventArgs e)
{
if (sender is Window window)
window.Closed -= Window_Closed;
this.Activate();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.WindowState != WindowState.Minimized)
{
Config.Default.MainWindow_Height = e.NewSize.Height;
Config.Default.MainWindow_Width = e.NewSize.Width;
}
}
private void Window_StateChanged(object sender, EventArgs e)
{
if (Config.Default.MainWindow_MinimizeToTray && this.WindowState == WindowState.Minimized)
{
this.Hide();
}
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
RconWindow.CloseAllWindows();
PlayerListWindow.CloseAllWindows();
ServerMonitorWindow.CloseAllWindows();
this.versionChecker.DisposeAsync().DoNotWait();
var installFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var backupFolder = IOUtils.NormalizePath(string.IsNullOrWhiteSpace(Config.Default.BackupPath)
? Path.Combine(Config.Default.DataPath, Config.Default.BackupRelativePath)
: Path.Combine(Config.Default.BackupPath));
SettingsUtils.BackupUserConfigSettings(Config.Default, "userconfig.json", installFolder, backupFolder);
SettingsUtils.BackupUserConfigSettings(CommonConfig.Default, "commonconfig.json", installFolder, backupFolder);
}
private void ResourceDictionaryChangedEvent(object source, ResourceDictionaryChangedEventArgs e)
{
if (IsAdministrator)
this.Title = _globalizer.GetResourceString("MainWindow_TitleWithAdmin");
else
this.Title = _globalizer.GetResourceString("MainWindow_Title");
this.scheduledTaskChecker.PostAction(CheckForScheduledTasks).DoNotWait();
}
private void PatchNotes_Click(object sender, RoutedEventArgs e)
{
var url = string.Empty;
if (AppInstance.BetaVersion)
url = Config.Default.ServerManagerVersionBetaFeedUrl;
else
url = Config.Default.ServerManagerVersionFeedUrl;
if (!string.IsNullOrWhiteSpace(url))
{
var window = new VersionFeedWindow(url);
window.Closed += Window_Closed;
window.Owner = this;
window.ShowDialog();
}
else
{
if (AppInstance.BetaVersion)
url = Config.Default.LatestServerManagerBetaPatchNotesUrl;
else
url = Config.Default.LatestServerManagerPatchNotesUrl;
if (string.IsNullOrWhiteSpace(url))
return;
Process.Start(url);
}
}
private void Donate_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Config.DonationUrl))
return;
var result = MessageBox.Show(_globalizer.GetResourceString("MainWindow_Donate_Label"), _globalizer.GetResourceString("MainWindow_Donate_Title"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Process.Start(Config.DonationUrl);
}
}
private void Help_Click(object sender, RoutedEventArgs args)
{
if (string.IsNullOrWhiteSpace(Config.Default.HelpUrl))
return;
Process.Start(Config.Default.HelpUrl);
}
private void OpenLogFolder_Click(object sender, RoutedEventArgs e)
{
var logFolder = App.GetLogFolder();
if (!Directory.Exists(logFolder))
logFolder = Config.Default.DataPath;
Process.Start("explorer.exe", logFolder);
}
private async void RefreshPublicIP_Click(object sender, RoutedEventArgs e)
{
try
{
await App.DiscoverMachinePublicIPAsync(true);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Refresh Public IP Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Settings_Click(object sender, RoutedEventArgs args)
{
var window = new SettingsWindow();
window.Closed += Window_Closed;
window.Owner = this;
window.ShowDialog();
}
private void GameData_Click(object sender, RoutedEventArgs e)
{
var window = new GameDataWindow();
window.Closed += Window_Closed;
window.Owner = this;
window.ShowDialog();
}
private void Plugins_Click(object sender, RoutedEventArgs e)
{
var window = new PluginsWindow();
window.Closed += Window_Closed;
window.Owner = this;
window.ShowDialog();
}
private void ServerMonitor_Click(object sender, RoutedEventArgs e)
{
var window = ServerMonitorWindow.GetWindow(ServerManager);
window.Closed += Window_Closed;
window.Show();
if (window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
}
window.Focus();
}
private async void SteamCMD_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show(_globalizer.GetResourceString("MainWindow_SteamCmd_Label"), _globalizer.GetResourceString("MainWindow_SteamCmd_Title"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
ProgressWindow window = null;
try
{
var updater = new SteamCmdUpdater();
var cancelSource = new CancellationTokenSource();
window = new ProgressWindow(_globalizer.GetResourceString("Progress_ReinstallSteamCmd_WindowTitle"));
window.Closed += Window_Closed;
window.Owner = this;
window.Show();
await Task.Delay(1000);
await updater.ReinstallSteamCmdAsync(Config.Default.DataPath, new Progress<SteamCmdUpdater.Update>(u =>
{
var resourceString = string.IsNullOrWhiteSpace(u.StatusKey) ? null : _globalizer.GetResourceString(u.StatusKey);
var message = resourceString != null ? $"{SteamCmdUpdater.OUTPUT_PREFIX} {resourceString}" : u.StatusKey;
window?.AddMessage(message);
if (u.FailureText != null)
{
message = string.Format(_globalizer.GetResourceString("MainWindow_SteamCmd_FailedLabel"), u.FailureText);
window?.AddMessage(message);
MessageBox.Show(message, _globalizer.GetResourceString("MainWindow_SteamCmd_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}), cancelSource.Token);
}
catch (Exception ex)
{
var message = string.Format(_globalizer.GetResourceString("MainWindow_SteamCmd_FailedLabel"), ex.Message);
window?.AddMessage(message);
MessageBox.Show(message, _globalizer.GetResourceString("MainWindow_SteamCmd_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
if (window != null)
window.CloseWindow();
}
}
}
private async void Upgrade_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show(String.Format(_globalizer.GetResourceString("MainWindow_Upgrade_Label"), this.LatestServerManagerVersion), _globalizer.GetResourceString("MainWindow_Upgrade_Title"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if(result == MessageBoxResult.Yes)
{
try
{
OverlayMessage.Content = _globalizer.GetResourceString("MainWindow_OverlayMessage_UpgradeLabel");
OverlayGrid.Visibility = Visibility.Visible;
await Task.Delay(500);
var process = Process.GetCurrentProcess();
if (process == null || process.HasExited)
throw new Exception("Application process could not be found or does not exist.");
var assemblyLocation = Assembly.GetEntryAssembly().Location;
var updaterFile = Path.Combine(Path.GetDirectoryName(assemblyLocation), Config.Default.UpdaterFile);
var newUpdaterFile = Path.Combine(Path.GetDirectoryName(assemblyLocation), $"New{Config.Default.UpdaterFile}");
// check if there is a new version of the updater file
if (System.IO.File.Exists(newUpdaterFile))
{
// file exists, rename the file, so that we use the new updater instead
try
{
File.Copy(newUpdaterFile, updaterFile, true);
await Task.Delay(1000);
File.Delete(newUpdaterFile);
}
catch (Exception ex)
{
// if error, then do nothing
Logger.Debug($"An error occurred trying to update the server manager updater. {ex.Message}");
}
}
if (!File.Exists(updaterFile))
throw new FileNotFoundException("The updater application could not be found or does not exist.");
var arguments = new string[]
{
process.Id.ToString().AsQuoted(),
App.Instance.BetaVersion ? Config.Default.LatestServerManagerBetaDownloadUrl.AsQuoted() : Config.Default.LatestServerManagerDownloadUrl.AsQuoted(),
Config.Default.UpdaterPrefix.AsQuoted(),
};
ProcessStartInfo info = new ProcessStartInfo()
{
FileName = updaterFile.AsQuoted(),
Arguments = string.Join(" ", arguments),
UseShellExecute = true,
CreateNoWindow = true,
};
var updaterProcess = Process.Start(info);
if (updaterProcess == null)
throw new Exception("Could not restart application.");
}
catch (Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("MainWindow_Upgrade_FailedLabel"), ex.Message, ex.StackTrace), _globalizer.GetResourceString("MainWindow_Upgrade_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
OverlayGrid.Visibility = Visibility.Collapsed;
}
}
}
public void Servers_AddNew(object sender, NewItemRequestedEventArgs e)
{
var index = this.ServerManager.AddNew();
((EO.Wpf.TabControl)e.Source).SelectedIndex = index;
}
public void Servers_Remove(object sender, TabItemCloseEventArgs args)
{
args.Canceled = true;
var server = ServerManager.Instance.Servers[args.ItemIndex];
var result = MessageBox.Show(_globalizer.GetResourceString("MainWindow_ProfileDelete_Label"), String.Format(_globalizer.GetResourceString("MainWindow_ProfileDelete_Title"), server.Profile.ProfileName), MessageBoxButton.YesNo, MessageBoxImage.Warning);
if(result == MessageBoxResult.Yes)
{
ServerManager.Instance.Remove(server, deleteProfile: true);
args.Canceled = false;
}
}
private void AutoBackupTaskRun_Click(object sender, RoutedEventArgs e)
{
var taskKey = TaskSchedulerUtils.ComputeKey(Config.Default.DataPath);
try
{
TaskSchedulerUtils.RunAutoBackup(taskKey, null);
}
catch (Exception)
{
// Ignore.
}
}
private void AutoBackupTaskState_Click(object sender, RoutedEventArgs e)
{
if (!IsAdministrator)
{
MessageBox.Show(_globalizer.GetResourceString("MainWindow_TaskAdminErrorLabel"), _globalizer.GetResourceString("MainWindow_TaskAdminErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var taskKey = TaskSchedulerUtils.ComputeKey(Config.Default.DataPath);
try
{
TaskSchedulerUtils.SetAutoBackupState(taskKey, null, null);
}
catch (Exception)
{
// Ignore.
}
}
private void AutoUpdateTaskRun_Click(object sender, RoutedEventArgs e)
{
var taskKey = TaskSchedulerUtils.ComputeKey(Config.Default.DataPath);
try
{
TaskSchedulerUtils.RunAutoUpdate(taskKey, null);
}
catch (Exception)
{
// Ignore.
}
}
private void AutoUpdateTaskState_Click(object sender, RoutedEventArgs e)
{
if (!IsAdministrator)
{
MessageBox.Show(_globalizer.GetResourceString("MainWindow_TaskAdminErrorLabel"), _globalizer.GetResourceString("MainWindow_TaskAdminErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var taskKey = TaskSchedulerUtils.ComputeKey(Config.Default.DataPath);
try
{
TaskSchedulerUtils.SetAutoUpdateState(taskKey, null, null);
}
catch (Exception)
{
// Ignore.
}
}
public ICommand ShowWindowCommand
{
get
{
return new RelayCommand<object>(
execute: (parameter) =>
{
this.Show();
this.WindowState = WindowState.Normal;
this.Activate();
},
canExecute: (parameter) =>
{
return true;
}
);
}
}
public ICommand StatusButtonCommand
{
get
{
return new RelayCommand<Server>(
execute: (server) =>
{
Debug.WriteLine($"{server.Profile.ProfileName}: {server.Runtime.Status}");
if (!Config.Default.ServerStatus_EnableActions)
return;
switch (server.Runtime.Status)
{
case ServerStatus.Stopped:
if (Config.Default.ServerStatus_ShowActionConfirmation && MessageBox.Show(_globalizer.GetResourceString("MainWindow_ServerStatus_StartServerActionLabel"), _globalizer.GetResourceString("MainWindow_ServerStatus_StartServerActionTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
StartServerAsync(server).DoNotWait();
break;
case ServerStatus.Running:
if (Config.Default.ServerStatus_ShowActionConfirmation && MessageBox.Show(_globalizer.GetResourceString("MainWindow_ServerStatus_ShutdownServerActionLabel"), _globalizer.GetResourceString("MainWindow_ServerStatus_ShutdownServerActionTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
ShutdownServerAsync(server).DoNotWait();
break;
}
},
canExecute: (server) => server != null && server.Profile != null && server.Runtime != null
);
}
}
private async Task CheckForUpdates()
{
string url = App.Instance.BetaVersion ? Config.Default.LatestServerManagerBetaVersionUrl : Config.Default.LatestServerManagerVersionUrl;
var newVersion = await NetworkUtils.GetLatestServerManagerVersion(url);
TaskUtils.RunOnUIThreadAsync(() =>
{
try
{
var appVersion = new Version();
Version.TryParse(App.Instance.Version, out appVersion);
this.LatestServerManagerVersion = newVersion;
this.NewServerManagerAvailable = appVersion < newVersion;
Logger.Info($"{nameof(CheckForUpdates)} performed.");
}
catch (Exception)
{
// Ignore.
}
}).DoNotWait();
await Task.Delay(Config.Default.UpdateCheckTime * 60 * 1000);
this.versionChecker.PostAction(CheckForUpdates).DoNotWait();
}
private async Task CheckForScheduledTasks()
{
var taskKey = TaskSchedulerUtils.ComputeKey(Config.Default.DataPath);
TaskUtils.RunOnUIThreadAsync(() =>
{
try
{
var backupState = TaskSchedulerUtils.TaskStateAutoBackup(taskKey, null, out DateTime backupnextRunTime);
var updateState = TaskSchedulerUtils.TaskStateAutoUpdate(taskKey, null, out DateTime updatenextRunTime);
this.AutoBackupState = backupState;
this.AutoUpdateState = updateState;
this.AutoBackupStateString = GetTaskStateString(AutoBackupState);
this.AutoUpdateStateString = GetTaskStateString(AutoUpdateState);
this.AutoBackupNextRunTime = backupnextRunTime == DateTime.MinValue ? string.Empty : $"{_globalizer.GetResourceString("MainWindow_TaskRunTimeLabel")} {backupnextRunTime:G}";
this.AutoUpdateNextRunTime = updatenextRunTime == DateTime.MinValue ? string.Empty : $"{_globalizer.GetResourceString("MainWindow_TaskRunTimeLabel")} {updatenextRunTime:G}";
Logger.Info($"{nameof(CheckForScheduledTasks)} performed");
}
catch (Exception)
{
// Ignore.
}
}).DoNotWait();
await Task.Delay(Config.Default.ScheduledTasksCheckTime * 1 * 1000);
this.scheduledTaskChecker.PostAction(CheckForScheduledTasks).DoNotWait();
}
private string GetTaskStateString(Microsoft.Win32.TaskScheduler.TaskState taskState)
{
switch (taskState)
{
case Microsoft.Win32.TaskScheduler.TaskState.Disabled:
return _globalizer.GetResourceString("MainWindow_TaskStateDisabledLabel");
case Microsoft.Win32.TaskScheduler.TaskState.Queued:
return _globalizer.GetResourceString("MainWindow_TaskStateQueuedLabel");
case Microsoft.Win32.TaskScheduler.TaskState.Ready:
return _globalizer.GetResourceString("MainWindow_TaskStateReadyLabel");
case Microsoft.Win32.TaskScheduler.TaskState.Running:
return _globalizer.GetResourceString("MainWindow_TaskStateRunningLabel");
case Microsoft.Win32.TaskScheduler.TaskState.Unknown:
return _globalizer.GetResourceString("MainWindow_TaskStateUnknownLabel");
default:
return _globalizer.GetResourceString("MainWindow_TaskStateUnknownLabel");
}
}
private async Task StartServerAsync(Server server)
{
if (server == null || server.Profile == null || server.Runtime == null || server.Runtime.Status != ServerStatus.Stopped)
return;
Mutex mutex = null;
bool createdNew = false;
try
{
// try to establish a mutex for the profile.
mutex = new Mutex(true, ServerApp.GetMutexName(server.Profile.InstallDirectory), out createdNew);
// check if the mutex was established
if (createdNew)
{
server.Profile.Save(false, false, null);
if (!server.Profile.Validate(false, out string validateMessage))
{
var outputMessage = _globalizer.GetResourceString("ProfileValidation_WarningLabel").Replace("{validateMessage}", validateMessage);
if (MessageBox.Show(outputMessage, _globalizer.GetResourceString("ProfileValidation_Title"), MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
return;
}
await server.StartAsync();
var startupMessage = Config.Default.Alert_ServerStartedMessage;
if (Config.Default.Alert_ServerStartedMessageIncludeIPandPort)
startupMessage += $" {Config.Default.MachinePublicIP}:{server.Profile.QueryPort}";
PluginHelper.Instance.ProcessAlert(AlertType.Startup, server.Profile.ProfileName, startupMessage);
await Task.Delay(2000);
}
else
{
// display an error message and exit
MessageBox.Show(_globalizer.GetResourceString("ServerSettings_StartServer_MutexFailedLabel"), _globalizer.GetResourceString("ServerSettings_StartServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_StartServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
if (mutex != null)
{
if (createdNew)
{
mutex.ReleaseMutex();
mutex.Dispose();
}
}
}
}
private async Task ShutdownServerAsync(Server server)
{
if (server == null || server.Profile == null || server.Runtime == null || server.Runtime.Status != ServerStatus.Running)
return;
try
{
var shutdownWindow = ShutdownWindow.OpenShutdownWindow(server);
if (shutdownWindow == null)
{
MessageBox.Show(_globalizer.GetResourceString("ServerSettings_ShutdownServer_AlreadyOpenLabel"), _globalizer.GetResourceString("ServerSettings_ShutdownServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
server.Runtime.UpdateServerStatus(ServerStatus.Stopping, AvailabilityStatus.Unavailable, false);
shutdownWindow.CloseShutdownWindowWhenFinished = true;
shutdownWindow.Owner = this;
shutdownWindow.Closed += Window_Closed;
shutdownWindow.Show();
await shutdownWindow.StartShutdownAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_ShutdownServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

View file

@ -0,0 +1,275 @@
<Window x:Class="ServerManagerTool.ModDetailsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
MinWidth="600" MinHeight="480" Width="900" Height="480" ResizeMode="CanResize" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Loaded="Window_Loaded" Closing="Window_Closing"
Name="ModDetailsUI" Icon="../Art/favicon.ico" Title="{DynamicResource ModDetails_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:IsNullOrWhiteSpaceValueConverter x:Key="IsNullOrWhiteSpaceValueConverter"/>
<cc:InvertBooleanConverter x:Key="InvertBooleanConverter"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}" MouseLeftButtonUp="OnMouseLeftButtonUp" MouseMove="OnMouseMove">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Width="22" Height="22" Margin="5,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="SaveMods_Click" ToolTip="{DynamicResource ModDetails_SaveModsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Save.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="1" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="AddMods_Click" ToolTip="{DynamicResource ModDetails_AddModsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Add.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="RemoveAllMods_Click" ToolTip="{DynamicResource ModDetails_RemoveAllModsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="3" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="RefreshMods_Click" ToolTip="{DynamicResource ModDetails_RefreshModsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Refresh.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="4" Width="22" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="ReloadMods_Click" ToolTip="{DynamicResource ModDetails_ReloadModsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Reload.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="5" Width="22" Height="22" Margin="0,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="LoadMods_Click" ToolTip="{DynamicResource ModDetails_LoadModsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/FolderOpen.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="6" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="PurgeModsFolder_Click" IsEnabled="{Binding Path=ModDetailsChanged, ElementName=ModDetailsUI, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ModDetails_PurgeModsFolderTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/FolderDelete.ico,Size=32}"/>
</Button>
<Button Grid.Row="0" Grid.Column="7" Width="22" Height="22" Margin="10,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="SaveModDetails_Click" ToolTip="{DynamicResource ModDetails_SaveModDetailsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Copy.ico,Size=32}"/>
</Button>
<StackPanel Grid.Row="0" Grid.Column="8" Orientation="Horizontal">
<TextBlock Margin="30,5,5,0" Text="{DynamicResource ModDetails_TotalCountLabel}" VerticalAlignment="Center" />
<TextBlock Margin="5,5,5,0" Text="{Binding ModDetails.Count}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="9" Margin="0,5,0,0" Orientation="Horizontal" Height="30" DataContext="{Binding}">
<TextBlock Margin="5,0,5,0" Text="{DynamicResource General_FilterLabel}" VerticalAlignment="Center" />
<TextBox Margin="5,0,5,0" Text="{Binding ModDetailsFilterString, Mode=TwoWay}" Width="200" Padding="2" VerticalAlignment="Center"/>
<Button Margin="5,0,5,0" Width="22" Height="22" HorizontalAlignment="Left" VerticalAlignment="Center" Click="FilterMods_Click" ToolTip="{DynamicResource General_FilterButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Filter.ico,Size=32}"/>
</Button>
<TextBlock Margin="5,0,5,0" Text="{Binding ModDetailsStatusMessage}" VerticalAlignment="Center" Foreground="SteelBlue" />
</StackPanel>
<DataGrid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="9" Name="ModDetailsGrid" ItemsSource="{Binding ModDetails}" Margin="5" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserSortColumns="True" RowHeaderWidth="0" SelectionMode="Single" PreviewMouseLeftButtonDown="OnMouseLeftButtonDown">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding IsValid}" Value="False">
<Setter Property="Background" Value="#FFB8E7F5" />
</DataTrigger>
<DataTrigger Binding="{Binding UpToDate}" Value="False">
<Setter Property="Background" Value="#FFF5DFB8" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Binding="{Binding Index, Mode=OneWay}">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource ModDetails_IndexColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTemplateColumn Width="Auto" MinWidth="100" CanUserSort="True" SortMemberPath="ModId">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ModDetails_ModIdColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink NavigateUri="{Binding ModUrl}" RequestNavigate="Mod_RequestNavigate">
<TextBlock Text="{Binding ModId, Mode=OneWay}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="1*" Binding="{Binding Title, Mode=OneWay}">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource ModDetails_TitleColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="Auto" MinWidth="80" Binding="{Binding ModTypeString, Mode=OneWay}">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource ModDetails_TypeColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="Auto" MinWidth="130" Binding="{Binding LastWriteTimeString, Mode=OneWay}" Header="{DynamicResource ModDetails_LastUpdatedColumnLabel}" SortMemberPath="LastWriteTimeSortString">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="Auto" MinWidth="130" Binding="{Binding TimeUpdatedString, Mode=OneWay}" Header="{DynamicResource ModDetails_TimeUpdatedColumnLabel}" SortMemberPath="TimeUpdatedSortString">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="Auto" MinWidth="80" Binding="{Binding TimeUpdated, Mode=OneWay}" Header="{DynamicResource ModDetails_TimestampColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="85" Binding="{Binding FolderSizeString, Mode=OneWay}" Header="{DynamicResource ModDetails_FolderSizeColumnLabel}" SortMemberPath="FolderSize">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="MoveModUp_Click" ToolTip="{DynamicResource ModDetails_MoveModUpTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Up.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFirst}" Value="True">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
<DataTrigger Binding="{Binding ModDetailsFilterString, ElementName=ModDetailsUI, Converter={StaticResource IsNullOrWhiteSpaceValueConverter}}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="MoveModDown_Click" ToolTip="{DynamicResource ModDetails_MoveModDownTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Down.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLast}" Value="True">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
<DataTrigger Binding="{Binding ModDetailsFilterString, ElementName=ModDetailsUI, Converter={StaticResource IsNullOrWhiteSpaceValueConverter}}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="RemoveMod_Click" ToolTip="{DynamicResource ModDetails_RemoveModTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="WriteTimestampMod_Click" ToolTip="{DynamicResource ModDetails_WriteTimestampModTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Edit.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsValid}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<!-- Drag and Drop Popup -->
<Popup Grid.Row="0" Grid.Column="0" x:Name="popup" IsHitTestVisible="False" Placement="RelativePoint" PlacementTarget="{Binding ElementName=ModDetailsUI}" 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}"/>
<TextBlock FontSize="14" VerticalAlignment="Center" Text="{Binding DraggedItem, ElementName=ModDetailsUI}" Margin="8,0,0,0" />
</StackPanel>
</Border>
</Popup>
</Grid>
</Window>

View file

@ -0,0 +1,647 @@
using NLog;
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Lib;
using ServerManagerTool.Utils;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Navigation;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for ModDetailsWindow.xaml
/// </summary>
public partial class ModDetailsWindow : Window
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public EventHandler<ProfileEventArgs> SavePerformed;
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private readonly ServerProfile _profile = null;
private WorkshopFilesWindow _workshopFilesWindow = null;
public static readonly DependencyProperty ModDetailsProperty = DependencyProperty.Register(nameof(ModDetails), typeof(ModDetailList), typeof(ModDetailsWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ModDetailsChangedProperty = DependencyProperty.Register(nameof(ModDetailsChanged), typeof(bool), typeof(ModDetailsWindow), new PropertyMetadata(false));
public static readonly DependencyProperty ModDetailsViewProperty = DependencyProperty.Register(nameof(ModDetailsView), typeof(ICollectionView), typeof(ModDetailsWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ModDetailsFilterStringProperty = DependencyProperty.Register(nameof(ModDetailsFilterString), typeof(string), typeof(ModDetailsWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty ModDetailsStatusMessageProperty = DependencyProperty.Register(nameof(ModDetailsStatusMessage), typeof(string), typeof(ModDetailsWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty WorkshopFilesProperty = DependencyProperty.Register(nameof(WorkshopFiles), typeof(WorkshopFileList), typeof(ModDetailsWindow), new PropertyMetadata(null));
public ModDetailsWindow(ServerProfile profile)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
_profile = profile;
this.Title = string.Format(_globalizer.GetResourceString("ModDetails_ProfileTitle"), _profile?.ProfileName);
this.DataContext = this;
}
public ModDetailList ModDetails
{
get { return GetValue(ModDetailsProperty) as ModDetailList; }
set
{
SetValue(ModDetailsProperty, value);
ModDetailsView = CollectionViewSource.GetDefaultView(ModDetails);
ModDetailsView.Filter = new Predicate<object>(Filter);
if (_workshopFilesWindow != null)
_workshopFilesWindow.UpdateModDetailsList(value);
}
}
public bool ModDetailsChanged
{
get { return (bool)GetValue(ModDetailsChangedProperty); }
set { SetValue(ModDetailsChangedProperty, value); }
}
public ICollectionView ModDetailsView
{
get { return GetValue(ModDetailsViewProperty) as ICollectionView; }
set { SetValue(ModDetailsViewProperty, value); }
}
public string ModDetailsFilterString
{
get { return (string)GetValue(ModDetailsFilterStringProperty); }
set { SetValue(ModDetailsFilterStringProperty, value); }
}
public string ModDetailsStatusMessage
{
get { return (string)GetValue(ModDetailsStatusMessageProperty); }
set { SetValue(ModDetailsStatusMessageProperty, value); }
}
public WorkshopFileList WorkshopFiles
{
get { return GetValue(WorkshopFilesProperty) as WorkshopFileList; }
set { SetValue(WorkshopFilesProperty, value); }
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
WorkshopFiles = await LoadWorkshopItemsAsync();
await LoadModsFromProfile();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
if (ModDetailsChanged)
{
var result = MessageBox.Show(_globalizer.GetResourceString("ModDetails_Unsaved_Label"), _globalizer.GetResourceString("ModDetails_Unsaved_Title"), MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
e.Cancel = true;
}
}
private async void ModDetails_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var cursor = this.Cursor;
try
{
ModDetailsChanged = true;
if (e.Action == NotifyCollectionChangedAction.Add)
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await LoadModsFromList();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Refresh_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private async void WorkshopFilesWindow_Closed(object sender, EventArgs e)
{
_workshopFilesWindow = null;
this.Activate();
try
{
WorkshopFiles = await LoadWorkshopItemsAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void Mod_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
var mod = ((ModDetail)((Hyperlink)e.Source).DataContext);
Process.Start(new ProcessStartInfo(mod.ModUrl));
e.Handled = true;
}
private void AddMods_Click(object sender, RoutedEventArgs e)
{
if (_workshopFilesWindow != null)
return;
_workshopFilesWindow = new WorkshopFilesWindow(this, _profile);
_workshopFilesWindow.Owner = this;
_workshopFilesWindow.Closed += WorkshopFilesWindow_Closed;
_workshopFilesWindow.Show();
}
private async void LoadMods_Click(object sender, RoutedEventArgs e)
{
if (_profile == null)
return;
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await LoadModsFromServerFolder();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private void MoveModDown_Click(object sender, RoutedEventArgs e)
{
var mod = ((ModDetail)((Button)e.Source).DataContext);
ModDetails.MoveDown(mod);
}
private void MoveModUp_Click(object sender, RoutedEventArgs e)
{
var mod = ((ModDetail)((Button)e.Source).DataContext);
ModDetails.MoveUp(mod);
}
private async void PurgeModsFolder_Click(object sender, RoutedEventArgs e)
{
if (_profile == null)
return;
var cursor = this.Cursor;
try
{
if (MessageBox.Show(_globalizer.GetResourceString("ModDetails_Purge_Label"), _globalizer.GetResourceString("ModDetails_Purge_Title"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
int folderCount;
int fileCount;
PurgeModsFromServerFolder(out folderCount, out fileCount);
var message = _globalizer.GetResourceString("ModDetails_Purge_SuccessLabel");
message = message.Replace("{folderCount}", folderCount.ToString());
message = message.Replace("{fileCount}", fileCount.ToString());
MessageBox.Show(message, _globalizer.GetResourceString("ModDetails_Purge_Title"), MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Purge_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private async void RefreshMods_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await LoadModsFromList();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Refresh_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private async void ReloadMods_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await LoadModsFromProfile();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Reload_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private void RemoveMod_Click(object sender, RoutedEventArgs e)
{
var mod = ((ModDetail)((Button)e.Source).DataContext);
ModDetails.Remove(mod);
}
private void RemoveAllMods_Click(object sender, RoutedEventArgs e)
{
ModDetails.Clear();
}
private async void SaveMods_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
string mapString;
string modIdString;
// check if there are any unknown mod types.
if (ModDetails.AnyUnknownModTypes)
{
if (MessageBox.Show(_globalizer.GetResourceString("ModDetails_Save_UnknownLabel"), _globalizer.GetResourceString("ModDetails_Save_UnknownTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
}
if (ModDetails.GetModStrings(out mapString, out modIdString))
{
if (mapString != null)
_profile.ServerMap = mapString;
_profile.ServerModIds = modIdString ?? string.Empty;
}
await LoadModsFromProfile();
OnSavePerformed();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_Save_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private void SaveModDetails_Click(object sender, RoutedEventArgs e)
{
var modDetails = string.Empty;
var delimiter = string.Empty;
foreach (var modDetail in ModDetails)
{
modDetails += $"{delimiter}{modDetail.Title} ({modDetail.ModId}){Environment.NewLine}{modDetail.ModUrl}";
delimiter = Environment.NewLine;
}
var window = new CommandLineWindow(modDetails);
window.OutputTextWrapping = TextWrapping.NoWrap;
window.Height = 500;
window.Title = _globalizer.GetResourceString("ModDetails_Clipboard_SaveTitle");
window.Owner = Window.GetWindow(this);
window.ShowDialog();
}
private async void WriteTimestampMod_Click(object sender, RoutedEventArgs e)
{
var mod = ((ModDetail)((Button)e.Source).DataContext);
if (mod == null)
return;
if (MessageBox.Show(_globalizer.GetResourceString("ModDetails_WriteTimestamp_ConfirmLabel"), _globalizer.GetResourceString("ModDetails_WriteTimestamp_ConfirmTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
var file = ModUtils.GetLatestModTimeFile(_profile?.InstallDirectory, mod.ModId);
var steamLastUpdated = mod?.TimeUpdated.ToString() ?? string.Empty;
File.WriteAllText(file, steamLastUpdated);
mod.PopulateExtended(Path.Combine(_profile.InstallDirectory, Config.Default.ServerModsRelativePath));
ModDetailsView?.Refresh();
}
catch (DirectoryNotFoundException)
{
MessageBox.Show(_globalizer.GetResourceString("ModDetails_WriteTimestamp_FailedLabel"), _globalizer.GetResourceString("ModDetails_WriteTimestamp_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ModDetails_WriteTimestamp_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
protected void OnSavePerformed()
{
SavePerformed?.Invoke(this, new ProfileEventArgs(_profile));
}
public int SelectedRowIndex()
{
return ModDetailsGrid.SelectedIndex;
}
private async Task LoadModsFromList()
{
// build a list of mods to be processed
var modIdList = new List<string>();
modIdList.AddRange(ModDetails.Select(m => m.ModId));
modIdList = ModUtils.ValidateModList(modIdList);
var response = await Task.Run(() => SteamUtils.GetSteamModDetails(modIdList));
var workshopFiles = WorkshopFiles;
var modsRootFolder = Path.Combine(_profile.InstallDirectory, Config.Default.ServerModsRelativePath);
var modDetails = ModDetailList.GetModDetails(modIdList, modsRootFolder, workshopFiles, response);
UpdateModDetailsList(modDetails);
ModDetailsStatusMessage = modDetails.Count > 0 && response?.publishedfiledetails == null ? _globalizer.GetResourceString("ModDetails_ModDetailFetchFailed_Label") : string.Empty;
ModDetailsChanged = true;
}
private async Task LoadModsFromProfile()
{
// build a list of mods to be processed
var modIdList = new List<string>();
modIdList.AddRange(ModUtils.GetModIdList(_profile.ServerModIds));
modIdList = ModUtils.ValidateModList(modIdList);
var response = await Task.Run(() => SteamUtils.GetSteamModDetails(modIdList));
var workshopFiles = WorkshopFiles;
var modsRootFolder = Path.Combine(_profile.InstallDirectory, Config.Default.ServerModsRelativePath);
var modDetails = ModDetailList.GetModDetails(modIdList, modsRootFolder, workshopFiles, response);
UpdateModDetailsList(modDetails);
ModDetailsStatusMessage = modDetails.Count > 0 && response?.publishedfiledetails == null ? _globalizer.GetResourceString("ModDetails_ModDetailFetchFailed_Label") : string.Empty;
ModDetailsChanged = false;
}
private async Task LoadModsFromServerFolder()
{
// build a list of mods to be processed
var modIdList = new List<string>();
var modDirectory = new DirectoryInfo(Path.Combine(_profile.InstallDirectory, Config.Default.ServerModsRelativePath));
foreach (var file in modDirectory.GetFiles("*.pak"))
{
var modId = Path.GetFileNameWithoutExtension(file.Name);
if (ModUtils.IsOfficialMod(modId))
continue;
modIdList.Add(modId);
}
modIdList = ModUtils.ValidateModList(modIdList);
var response = await Task.Run(() => SteamUtils.GetSteamModDetails(modIdList));
var workshopFiles = WorkshopFiles;
var modsRootFolder = Path.Combine(_profile.InstallDirectory, Config.Default.ServerModsRelativePath);
var modDetails = ModDetailList.GetModDetails(modIdList, modsRootFolder, workshopFiles, response);
UpdateModDetailsList(modDetails);
ModDetailsStatusMessage = modDetails.Count > 0 && response?.publishedfiledetails == null ? _globalizer.GetResourceString("ModDetails_ModDetailFetchFailed_Label") : string.Empty;
ModDetailsChanged = true;
}
private async Task<WorkshopFileList> LoadWorkshopItemsAsync()
{
WorkshopFileDetailResponse localCache = null;
await Task.Run(() => {
var file = Path.Combine(Config.Default.DataPath, Config.Default.WorkshopCacheFile);
// try to load the cache file.
localCache = WorkshopFileDetailResponse.Load(file);
});
return WorkshopFileList.GetList(localCache);
}
private void PurgeModsFromServerFolder(out int folderCount, out int fileCount)
{
folderCount = 0;
fileCount = 0;
// build a list of mods to be processed
var modIdList = ModDetails.Select(m => m.ModId).ToList();
var modDirectory = new DirectoryInfo(Path.Combine(_profile.InstallDirectory, Config.Default.ServerModsRelativePath));
if (Directory.Exists(modDirectory.FullName))
{
foreach (var file in modDirectory.GetFiles("*.pak"))
{
var modId = Path.GetFileNameWithoutExtension(file.Name);
if (ModUtils.IsOfficialMod(modId))
continue;
if (modIdList.Contains(modId))
continue;
file.Delete();
fileCount++;
var timeFile = Path.ChangeExtension(file.FullName, ".txt");
if (File.Exists(timeFile))
File.Delete(timeFile);
}
}
}
private void UpdateModDetailsList(ModDetailList modDetails)
{
if (ModDetails != null)
ModDetails.CollectionChanged -= ModDetails_CollectionChanged;
ModDetails = modDetails ?? new ModDetailList();
if (ModDetails != null)
ModDetails.CollectionChanged += ModDetails_CollectionChanged;
ModDetailsView?.Refresh();
}
#region Drag and Drop
public static readonly DependencyProperty DraggedItemProperty = DependencyProperty.Register(nameof(DraggedItem), typeof(ModDetail), typeof(ModDetailsWindow), new PropertyMetadata(null));
public ModDetail DraggedItem
{
get { return (ModDetail)GetValue(DraggedItemProperty); }
set { SetValue(DraggedItemProperty, value); }
}
public bool IsDragging { get; set; }
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// check fi the column is a template column (no drag-n-drop for those column types)
var cell = WindowUtils.TryFindFromPoint<DataGridCell>((UIElement)sender, e.GetPosition(ModDetailsGrid));
if (cell == null || cell.Column is DataGridTemplateColumn) return;
// check if we have a valid row
var row = WindowUtils.TryFindFromPoint<DataGridRow>((UIElement)sender, e.GetPosition(ModDetailsGrid));
if (row == null) return;
// set flag that indicates we're capturing mouse movements
IsDragging = true;
DraggedItem = (ModDetail)row.Item;
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!IsDragging)
{
if (popup.IsOpen)
popup.IsOpen = false;
return;
}
//get the target item
var targetItem = (ModDetail)ModDetailsGrid.SelectedItem;
if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
{
//get target index
var targetIndex = ModDetails.IndexOf(targetItem);
//move source at the target's location
ModDetails.Move(DraggedItem, targetIndex);
//select the dropped item
ModDetailsGrid.SelectedItem = DraggedItem;
}
//reset
ResetDragDrop();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!IsDragging || e.LeftButton != MouseButtonState.Pressed)
{
if (popup.IsOpen)
popup.IsOpen = false;
return;
}
// display the popup if it hasn't been opened yet
if (!popup.IsOpen)
{
// switch to read-only mode
ModDetailsGrid.IsReadOnly = true;
// make sure the popup is visible
popup.IsOpen = true;
}
var popupSize = new Size(popup.ActualWidth, popup.ActualHeight);
popup.PlacementRectangle = new Rect(e.GetPosition(this), popupSize);
// make sure the row under the grid is being selected
var position = e.GetPosition(ModDetailsGrid);
var row = WindowUtils.TryFindFromPoint<DataGridRow>(ModDetailsGrid, position);
if (row != null) ModDetailsGrid.SelectedItem = row.Item;
}
private void ResetDragDrop()
{
IsDragging = false;
popup.IsOpen = false;
ModDetailsGrid.IsReadOnly = false;
}
#endregion
#region Filter
private void FilterMods_Click(object sender, RoutedEventArgs e)
{
ModDetailsView?.Refresh();
}
public bool Filter(object obj)
{
var data = obj as ModDetail;
if (data == null)
return false;
var filterString = ModDetailsFilterString.ToLower();
if (string.IsNullOrWhiteSpace(filterString))
return true;
return data.ModId.Contains(filterString) || data.TitleFilterString.Contains(filterString);
}
#endregion
}
}

View file

@ -0,0 +1,177 @@
<Window x:Class="ServerManagerTool.PlayerListWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:en="clr-namespace:ServerManagerTool.Enums"
xmlns:vm="clr-namespace:ServerManagerTool.Lib.ViewModel"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
MinWidth="300" MinHeight="200" Width="300" Height="500" ResizeMode="CanResizeWithGrip" WindowStyle="ToolWindow"
SizeChanged="Window_SizeChanged" LocationChanged="Window_LocationChanged"
Name="PlayerList" Icon="../Art/favicon.ico" Title="{Binding PlayerListParameters.WindowTitle}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:EnumConverter x:Key="EnumConverter"/>
<cc:EnumFlagsConverter x:Key="EnumFlagsConverter"/>
<cc:IsNullOrWhiteSpaceValueConverter x:Key="IsNullOrWhiteSpaceValueConverter"/>
<cc:IsNullValueConverter x:Key="IsNullValueConverter"/>
<cc:NullValueConverter x:Key="NullValueConverter"/>
<SolidColorBrush x:Key="HeaderBrush" Color="#FFF0F0F0"/>
<Style x:Key="PlayerStatus" TargetType="Border">
<Setter Property="BorderBrush" Value="Gray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsOnline}" Value="True">
<Setter Property="BorderBrush" Value="LightGreen"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsOnline}" Value="False" />
<Condition Binding="{Binding IsValid}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Orange"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsOnline}" Value="False" />
<Condition Binding="{Binding IsWhitelisted}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="#0066CC"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="GuildName" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding GuildName, Converter={StaticResource IsNullOrWhiteSpaceValueConverter}}" Value="True">
<Setter Property="Content" Value="No clan"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="Foreground" Value="LightGray"/>
</DataTrigger>
<DataTrigger Binding="{Binding GuildName, Converter={StaticResource IsNullOrWhiteSpaceValueConverter}}" Value="False">
<Setter Property="Content" Value="{Binding PlayerData.Guild.GuildName}"/>
<Setter Property="FontStyle" Value="Normal"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<DockPanel x:Name="dockPanel">
<Border DockPanel.Dock="Top" BorderThickness="1" BorderBrush="LightGray" Margin="2" Background="{StaticResource HeaderBrush}">
<DockPanel Margin="2,2,2,1" LastChildFill="False">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<Menu VerticalAlignment="Center" Margin="1" Background="{StaticResource HeaderBrush}">
<MenuItem Header="{DynamicResource RCON_Menu_Players}">
<MenuItem Header="{DynamicResource RCON_Menu_Sort}">
<MenuItem Header="{DynamicResource RCON_Menu_SortOnline}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.Online}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=Online}"/>
<MenuItem Header="{DynamicResource RCON_Menu_SortName}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.Name}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=Name}"/>
<MenuItem Header="{DynamicResource RCON_Menu_SortTribe}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.Tribe}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=Tribe}"/>
<MenuItem Header="{DynamicResource RCON_Menu_SortLastOnline}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.LastOnline}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=LastOnline}"/>
</MenuItem>
<MenuItem Header="{DynamicResource RCON_Menu_Filter}">
<MenuItem Header="{DynamicResource RCON_Menu_FilterOnline}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Online}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Online, Mode=OneWay}"/>
<MenuItem Header="{DynamicResource RCON_Menu_FilterOffline}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Offline}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Offline, Mode=OneWay}"/>
<MenuItem Header="{DynamicResource RCON_Menu_FilterWhitelisted}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Whitelisted}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Whitelisted, Mode=OneWay}"/>
<MenuItem Header="{DynamicResource RCON_Menu_FilterInvalid}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Invalid}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Invalid, Mode=OneWay}"/>
</MenuItem>
</MenuItem>
</Menu>
</StackPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Right">
<Label Content="{DynamicResource RCON_PlayersLabel}" Margin="5,0,0,0"/>
<Label Content="{Binding ServerPlayers.CountOnlinePlayers, FallbackValue=0}"/>
<Label Content="{DynamicResource RCON_PlayersSeparatorLabel}" Name="PlayerCountSeparator"/>
<Label Content="{Binding PlayerListParameters.MaxPlayers, FallbackValue=0}" Name="MaxPlayerLabel"/>
</StackPanel>
</DockPanel>
</Border>
<DockPanel DockPanel.Dock="Right" MinWidth="200" Margin="1,1,2,1" DataContext="{Binding}">
<DockPanel DockPanel.Dock="Top">
<Label DockPanel.Dock="Left" Content="{DynamicResource General_FilterLabel}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button DockPanel.Dock="Right" Margin="5,0,5,0" Width="22" Height="22" HorizontalAlignment="Right" VerticalAlignment="Center" Click="FilterPlayerList_Click" ToolTip="{DynamicResource General_FilterButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Filter.ico,Size=32}"/>
</Button>
<TextBox DockPanel.Dock="Left" Text="{Binding PlayerListFilterString, Mode=TwoWay}" BorderBrush="LightGray" VerticalAlignment="Center" VerticalContentAlignment="Center" IsTabStop="True" Margin="0" Padding="2"/>
</DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Label Content="{DynamicResource PlayerList_ProfileCountLabel}" HorizontalAlignment="Center"/>
<Label Content="{Binding ServerPlayers.Players.Count, FallbackValue=0}" HorizontalAlignment="Center"/>
<Label Content="{DynamicResource PlayerList_ProfileInvalidCountLabel}" HorizontalAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ServerPlayers.CountInvalidPlayers, FallbackValue=0, Converter={cc:GreaterThanIntValueConverter 0}}" Value="true">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Label Content="{Binding ServerPlayers.CountInvalidPlayers, FallbackValue=0}" HorizontalAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ServerPlayers.CountInvalidPlayers, FallbackValue=0, Converter={cc:GreaterThanIntValueConverter 0}}" Value="true">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackPanel>
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding ServerPlayers.Players}" BorderBrush="LightGray" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox.Resources>
<clib:BindingProxy x:Key="proxy" Data="{Binding ElementName=PlayerList}"/>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{DynamicResource PlayerList_Menu_ViewProfile}" Command="{Binding Source={StaticResource proxy}, Path=Data.ViewPlayerProfileCommand}" CommandParameter="{Binding}" />
<MenuItem Header="{DynamicResource PlayerList_Menu_ViewTribe}" Command="{Binding Source={StaticResource proxy}, Path=Data.ViewPlayerTribeCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:PlayerInfo}">
<Border BorderThickness="2" Padding="1" Style="{DynamicResource PlayerStatus}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" HorizontalAlignment="Stretch">
<StackPanel Orientation="Horizontal" Margin="0" ToolTip="{Binding PlayerId}">
<Label Content="{Binding CharacterName}" Padding="0,-1,0,-1"/>
<TextBlock Text="{Binding PlayerData.Level, StringFormat=({0})}" Margin="5,0,0,0" Padding="0,-1,0,-1" />
</StackPanel>
<Label Padding="0,-1,0,-1" Style="{DynamicResource GuildName}"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</DockPanel>
</Grid>
</Window>

View file

@ -0,0 +1,389 @@
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Lib.ViewModel;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for PlayerListWindow.xaml
/// </summary>
public partial class PlayerListWindow : Window
{
private static Dictionary<Server, PlayerListWindow> Windows = new Dictionary<Server, PlayerListWindow>();
private GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty ConfigProperty = DependencyProperty.Register(nameof(Config), typeof(Config), typeof(PlayerListWindow), new PropertyMetadata(Config.Default));
public static readonly DependencyProperty PlayerFilteringProperty = DependencyProperty.Register(nameof(PlayerFiltering), typeof(PlayerFilterType), typeof(PlayerListWindow), new PropertyMetadata(PlayerFilterType.Online | PlayerFilterType.Offline));
public static readonly DependencyProperty PlayerListParametersProperty = DependencyProperty.Register(nameof(PlayerListParameters), typeof(PlayerListParameters), typeof(PlayerListWindow), new PropertyMetadata(null));
public static readonly DependencyProperty PlayerListFilterStringProperty = DependencyProperty.Register(nameof(PlayerListFilterString), typeof(string), typeof(PlayerListWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty PlayerListViewProperty = DependencyProperty.Register(nameof(PlayerListView), typeof(ICollectionView), typeof(PlayerListWindow), new PropertyMetadata(null));
public static readonly DependencyProperty PlayerSortingProperty = DependencyProperty.Register(nameof(PlayerSorting), typeof(PlayerSortType), typeof(PlayerListWindow), new PropertyMetadata(PlayerSortType.Name));
public static readonly DependencyProperty ServerPlayersProperty = DependencyProperty.Register(nameof(ServerPlayers), typeof(ServerPlayers), typeof(PlayerListWindow), new PropertyMetadata(null));
public PlayerListWindow(PlayerListParameters parameters)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.PlayerFiltering = (PlayerFilterType)Config.Default.PlayerListFilter;
this.PlayerSorting = (PlayerSortType)Config.Default.PlayerListSort;
this.PlayerListParameters = parameters;
this.ServerPlayers = new ServerPlayers(parameters);
this.ServerPlayers.Players.CollectionChanged += Players_CollectionChanged;
this.ServerPlayers.PlayersCollectionUpdated += Players_CollectionUpdated;
this.PlayerListView = CollectionViewSource.GetDefaultView(this.ServerPlayers.Players);
this.PlayerListView.Filter = new Predicate<object>(PlayerListFilter);
if (this.PlayerListParameters?.Server?.Runtime != null)
{
this.PlayerListParameters.Server.Runtime.StatusUpdate += Runtime_StatusUpdate;
}
this.DataContext = this;
if (this.PlayerListParameters.WindowExtents.Width > 50 && this.PlayerListParameters.WindowExtents.Height > 50)
{
this.Left = this.PlayerListParameters.WindowExtents.Left;
this.Top = this.PlayerListParameters.WindowExtents.Top;
this.Width = this.PlayerListParameters.WindowExtents.Width;
this.Height = this.PlayerListParameters.WindowExtents.Height;
// Fix issues where the window was saved while offscreen.
if (this.Left == -32000)
{
this.Left = 0;
}
if (this.Top == -32000)
{
this.Top = 0;
}
}
}
public Config Config
{
get { return (Config)GetValue(ConfigProperty); }
set { SetValue(ConfigProperty, value); }
}
public PlayerListParameters PlayerListParameters
{
get { return (PlayerListParameters)GetValue(PlayerListParametersProperty); }
set { SetValue(PlayerListParametersProperty, value); }
}
public PlayerFilterType PlayerFiltering
{
get { return (PlayerFilterType)GetValue(PlayerFilteringProperty); }
set { SetValue(PlayerFilteringProperty, value); }
}
public string PlayerListFilterString
{
get { return (string)GetValue(PlayerListFilterStringProperty); }
set { SetValue(PlayerListFilterStringProperty, value); }
}
public ICollectionView PlayerListView
{
get { return (ICollectionView)GetValue(PlayerListViewProperty); }
set { SetValue(PlayerListViewProperty, value); }
}
public PlayerSortType PlayerSorting
{
get { return (PlayerSortType)GetValue(PlayerSortingProperty); }
set { SetValue(PlayerSortingProperty, value); }
}
public ServerPlayers ServerPlayers
{
get { return (ServerPlayers)GetValue(ServerPlayersProperty); }
set { SetValue(ServerPlayersProperty, value); }
}
public ICommand CopyIDCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) =>
{
try
{
System.Windows.Clipboard.SetText(player.PlayerId.ToString());
MessageBox.Show($"{_globalizer.GetResourceString("RCON_CopyIdLabel")} {player.PlayerName}", _globalizer.GetResourceString("RCON_CopyIdTitle"), MessageBoxButton.OK);
}
catch
{
MessageBox.Show($"{_globalizer.GetResourceString("RCON_ClipboardErrorLabel")}", _globalizer.GetResourceString("RCON_ClipboardErrorTitle"), MessageBoxButton.OK);
}
},
canExecute: (player) => player != null
);
}
}
public ICommand CopyPlayerIDCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) =>
{
if (player.PlayerData != null)
{
try
{
System.Windows.Clipboard.SetText(player.PlayerData.CharacterId.ToString());
MessageBox.Show($"{_globalizer.GetResourceString("RCON_CopyPlayerIdLabel")} {player.PlayerName}", _globalizer.GetResourceString("RCON_CopyPlayerIdTitle"), MessageBoxButton.OK);
}
catch
{
MessageBox.Show($"{_globalizer.GetResourceString("RCON__ClipboardErrorLabel")}", _globalizer.GetResourceString("RCON__ClipboardErrorTitle"), MessageBoxButton.OK);
}
}
},
canExecute: (player) => player?.PlayerData != null && player.IsValid
);
}
}
public ICommand FilterPlayersCommand
{
get
{
return new RelayCommand<PlayerFilterType>(
execute: (filter) =>
{
this.PlayerFiltering ^= filter;
Config.Default.PlayerListFilter = (int)this.PlayerFiltering;
this.PlayerListView.Refresh();
},
canExecute: (filter) => true
);
}
}
public ICommand SortPlayersCommand
{
get
{
return new RelayCommand<PlayerSortType>(
execute: (sort) =>
{
Config.Default.PlayerListSort = (int)this.PlayerSorting;
SortPlayers();
},
canExecute: (sort) => true
);
}
}
public ICommand ViewPlayerProfileCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) => {
var savedFilesPath = ServerProfile.GetProfileSavePath(this.PlayerListParameters.InstallDirectory);
var window = new PlayerProfileWindow(player, savedFilesPath)
{
Owner = this
};
window.ShowDialog();
},
canExecute: (player) => player?.PlayerData != null
);
}
}
public ICommand ViewPlayerTribeCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) => {
var savedFilesPath = ServerProfile.GetProfileSavePath(this.PlayerListParameters.InstallDirectory);
var window = new GuildProfileWindow(player, this.ServerPlayers.Players, savedFilesPath)
{
Owner = this
};
window.ShowDialog();
},
canExecute: (player) => player?.PlayerData != null && !string.IsNullOrWhiteSpace(player.GuildName)
);
}
}
private void Players_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
PlayerListView?.Refresh();
}
private void Players_CollectionUpdated(object sender, EventArgs e)
{
this.PlayerListView = CollectionViewSource.GetDefaultView(this.ServerPlayers.Players);
this.PlayerListView.Filter = new Predicate<object>(PlayerListFilter);
SortPlayers();
PlayerListView?.Refresh();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.WindowState != WindowState.Minimized)
{
var savedRect = this.PlayerListParameters.WindowExtents;
this.PlayerListParameters.WindowExtents = new Rect(savedRect.Location, e.NewSize);
}
}
private void Window_LocationChanged(object sender, EventArgs e)
{
if (this.WindowState != WindowState.Minimized && this.Left != -32000 && this.Top != -32000)
{
var savedRect = this.PlayerListParameters.WindowExtents;
this.PlayerListParameters.WindowExtents = new Rect(new Point(this.Left, this.Top), savedRect.Size);
if (this.PlayerListParameters.Server != null)
{
this.PlayerListParameters.Server.Profile.PlayerListWindowExtents = this.PlayerListParameters.WindowExtents;
}
}
}
private void Runtime_StatusUpdate(object sender, EventArgs eventArgs)
{
this.PlayerListParameters.ProfileName = this.PlayerListParameters?.Server?.Runtime?.ProfileSnapshot.ProfileName ?? "<unknown>";
this.PlayerListParameters.MaxPlayers = this.PlayerListParameters?.Server?.Runtime?.MaxPlayers ?? 0;
}
public static void CloseAllWindows()
{
var windows = Windows.Values.ToList();
foreach (var window in windows)
{
if (window.IsLoaded)
window.Close();
}
windows.Clear();
}
public static PlayerListWindow GetWindowForServer(Server server)
{
if (!Windows.TryGetValue(server, out PlayerListWindow window) || !window.IsLoaded)
{
window = new PlayerListWindow(new PlayerListParameters()
{
WindowTitle = String.Format(GlobalizedApplication.Instance.GetResourceString("PlayerList_TitleLabel"), server.Runtime.ProfileSnapshot.ProfileName),
WindowExtents = server.Profile.PlayerListWindowExtents,
Server = server,
InstallDirectory = server.Runtime.ProfileSnapshot.InstallDirectory,
GameFile = server.Runtime.ProfileSnapshot.GameFile,
ProfileName = server.Runtime.ProfileSnapshot.ProfileName,
ProfileId = server.Runtime.ProfileSnapshot.ProfileId,
MaxPlayers = server.Runtime.MaxPlayers,
});
Windows[server] = window;
}
return window;
}
protected override void OnClosing(CancelEventArgs e)
{
this.ServerPlayers.PlayersCollectionUpdated -= Players_CollectionUpdated;
this.ServerPlayers.Players.CollectionChanged -= Players_CollectionChanged;
this.ServerPlayers.Dispose();
if (this.PlayerListParameters?.Server != null)
{
Windows.TryGetValue(this.PlayerListParameters.Server, out PlayerListWindow window);
if (window != null)
{
Windows.Remove(this.PlayerListParameters.Server);
}
}
base.OnClosing(e);
}
public void SortPlayers()
{
this.PlayerListView.SortDescriptions.Clear();
switch (this.PlayerSorting)
{
case PlayerSortType.Name:
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
case PlayerSortType.Online:
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.IsOnline), ListSortDirection.Descending));
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
case PlayerSortType.Tribe:
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.GuildName), ListSortDirection.Ascending));
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
case PlayerSortType.LastOnline:
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.LastOnline), ListSortDirection.Descending));
this.PlayerListView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
}
}
#region Filtering
private void FilterPlayerList_Click(object sender, RoutedEventArgs e)
{
PlayerListView?.Refresh();
}
public bool PlayerListFilter(object obj)
{
var player = obj as PlayerInfo;
if (player == null)
return false;
var result = (this.PlayerFiltering.HasFlag(PlayerFilterType.Online) && player.IsOnline) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Offline) && !player.IsOnline) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Admin) && player.IsAdmin) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Whitelisted) && player.IsWhitelisted) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Invalid) && !player.IsValid);
if (!result)
return false;
var filterString = PlayerListFilterString.ToLower();
if (string.IsNullOrWhiteSpace(filterString))
return true;
result = player.PlatformNameFilterString != null && player.PlatformNameFilterString.Contains(filterString) ||
player.GuildNameFilterString != null && player.GuildNameFilterString.Contains(filterString) ||
player.CharacterNameFilterString != null && player.CharacterNameFilterString.Contains(filterString);
return result;
}
#endregion
}
}

View file

@ -0,0 +1,173 @@
<Window x:Class="ServerManagerTool.PlayerProfileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
Width="500" ResizeMode="NoResize" SizeToContent="Height" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Icon="../Art/favicon.ico" Title="{Binding WindowTitle}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:NullValueConverter x:Key="NullValueConverter"/>
<cc:UnixTimeToDateTimeConverter x:Key="UnixTimeToDateTimeConverter"/>
<BitmapImage x:Key="NoAvatar" UriSource="../Art/NoAvatar.png" />
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
</Style>
<Style x:Key="Online" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Player.IsOnline}" Value="True">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
<DataTrigger Binding="{Binding Player.IsOnline}" Value="False">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="CommunityBanned" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding PlayerData.CommunityBanned}" Value="False">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding PlayerData.CommunityBanned}" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="VACBanned" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding PlayerData.VACBanned}" Value="False">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding PlayerData.VACBanned}" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="Banned" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Player.IsBanned}" Value="False">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
<DataTrigger Binding="{Binding Player.IsBanned}" Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="Whitelisted" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger Binding="{Binding Player.IsWhitelisted}" Value="True">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
<DataTrigger Binding="{Binding Player.IsWhitelisted}" Value="False">
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<StackPanel Margin="3" CanVerticallyScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource Profile_OnlineLabel}"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding Player.IsOnline}" Style="{DynamicResource Online}"/>
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Profile_LastOnlineLabel}"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding Player.LastOnline, Converter={StaticResource UnixTimeToDateTimeConverter}}"/>
</Grid>
<GroupBox HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{DynamicResource Profile_PlayerSectionLabel}" VerticalAlignment="Center" FontWeight="Bold" FontSize="13.333"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="{DynamicResource Profile_IdLabel}"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding PlayerData.CharacterId}"/>
<Label Grid.Row="1" Content="{DynamicResource Profile_NameLabel}"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding PlayerData.CharacterName}"/>
<Label Grid.Row="2" Content="{DynamicResource Profile_LevelLabel}"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding PlayerData.Level}"/>
</Grid>
</GroupBox>
<GroupBox HorizontalAlignment="Stretch">
<GroupBox.Header>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{DynamicResource Profile_TribeSectionLabel}" VerticalAlignment="Center" FontWeight="Bold" FontSize="13.333"/>
</GroupBox.Header>
<GroupBox.Style>
<Style BasedOn="{StaticResource GroupBoxStyle}" TargetType="{x:Type GroupBox}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding PlayerData.GuildId}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="{DynamicResource Profile_IdLabel}"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding GuildData.GuildId}"/>
<Label Grid.Row="1" Content="{DynamicResource Profile_NameLabel}"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding GuildData.GuildName}"/>
<Label Grid.Row="2" Content="{DynamicResource Profile_TribeOwnerLabel}"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding GuildOwner}"/>
</Grid>
</GroupBox>
</StackPanel>
</Grid>
</Window>

View file

@ -0,0 +1,67 @@
using ConanData;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Lib.ViewModel;
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for PlayerProfileWindow.xaml
/// </summary>
public partial class PlayerProfileWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public PlayerProfileWindow(PlayerInfo player, String serverFolder)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.Player = player;
this.ServerFolder = serverFolder;
this.DataContext = this;
}
public PlayerInfo Player
{
get;
private set;
}
public String ServerFolder
{
get;
private set;
}
public PlayerData PlayerData => Player?.PlayerData;
public GuildData GuildData => Player?.PlayerData?.Guild;
public Boolean IsGuildOwner => PlayerData != null && GuildData != null && GuildData.OwnerId == PlayerData.CharacterId;
public String GuildOwner => GuildData != null && GuildData.Owner != null ? $"{GuildData.Owner.CharacterName}" : null;
public String WindowTitle => String.Format(_globalizer.GetResourceString("Profile_WindowTitle_Player"), Player.PlayerName);
public ICommand ExplorerLinkCommand
{
get
{
return new RelayCommand<String>(
execute: (action) =>
{
if (String.IsNullOrWhiteSpace(action)) return;
Process.Start("explorer.exe", action);
},
canExecute: (action) => true
);
}
}
}
}

View file

@ -0,0 +1,115 @@
<Window x:Class="ServerManagerTool.PluginsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
MinWidth="640" MinHeight="480" Width="640" Height="480" ResizeMode="CanResize" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Icon="../Art/favicon.ico" Title="{DynamicResource PluginsWindow_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" HorizontalAlignment="Stretch" Style="{StaticResource GroupBoxStyle}">
<GroupBox.Header>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Button Width="22" Height="22" Click="AddPlugin_Click" ToolTip="{DynamicResource PluginsWindow_AddPluginTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Add.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="ClearPlugins_Click" Margin="10,0,0,0" ToolTip="{DynamicResource PluginsWindow_ClearPluginsTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="OpenPluginsFolder_Click" Margin="10,0,0,0" ToolTip="{DynamicResource PluginsWindow_OpenPluginsFolderTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/FolderOpen.ico,Size=32}"/>
</Button>
<Button Width="22" Height="22" Click="PluginsForum_Click" Margin="20,0,0,0" ToolTip="{DynamicResource PluginsWindow_PluginsForumTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Help.ico,Size=32}"/>
</Button>
</StackPanel>
</GroupBox.Header>
<DataGrid ItemsSource="{Binding PluginHelperInstance.Plugins}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserSortColumns="true" SelectionMode="Single" CanUserResizeColumns="False" CanUserResizeRows="False" RowHeaderWidth="25">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
</Style.Resources>
</Style>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridTextColumn Width="4*" Binding="{Binding Plugin.PluginName}" IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource PluginsWindow_NameColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="2*" Binding="{Binding PluginType}" IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource PluginsWindow_TypeColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="1*" Binding="{Binding Plugin.PluginVersion}" IsReadOnly="True">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource PluginsWindow_VersionColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="ConfigPlugin_Click" ToolTip="{DynamicResource PluginsWindow_ConfigPluginTooltip}">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Plugin.HasConfigForm}" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Settings.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" IsTabStop="False" HorizontalAlignment="Center" VerticalAlignment="Center" Click="RemovePlugin_Click" ToolTip="{DynamicResource PluginsWindow_RemovePluginTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
</Window>

View file

@ -0,0 +1,124 @@
using Microsoft.WindowsAPICodePack.Dialogs;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Plugin.Common;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for PluginsWindow.xaml
/// </summary>
public partial class PluginsWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty PluginHelperInstanceProperty = DependencyProperty.Register(nameof(PluginHelperInstance), typeof(PluginHelper), typeof(PluginsWindow));
public PluginsWindow()
{
this.PluginHelperInstance = PluginHelper.Instance;
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.DataContext = this;
}
public PluginHelper PluginHelperInstance
{
get { return GetValue(PluginHelperInstanceProperty) as PluginHelper; }
set { SetValue(PluginHelperInstanceProperty, value); }
}
private void AddPlugin_Click(object sender, RoutedEventArgs e)
{
var dialog = new CommonOpenFileDialog();
dialog.Title = GlobalizedApplication.Instance.GetResourceString("PluginsWindow_AddDialogTitle");
dialog.DefaultExtension = GlobalizedApplication.Instance.GetResourceString("PluginsWindow_PluginDefaultExtension");
dialog.Filters.Add(new CommonFileDialogFilter(GlobalizedApplication.Instance.GetResourceString("PluginsWindow_AddFilterLabel"), GlobalizedApplication.Instance.GetResourceString("PluginsWindow_AddFilterExtension")));
if (dialog == null || dialog.ShowDialog(this) != CommonFileDialogResult.Ok)
return;
try
{
var installPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
PluginHelperInstance.AddPlugin(installPath, dialog.FileName);
}
catch (PluginException ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("PluginsWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
catch
{
MessageBox.Show(_globalizer.GetResourceString("PluginsWindow_AddErrorLabel"), _globalizer.GetResourceString("PluginsWindow_AddErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ClearPlugins_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show(_globalizer.GetResourceString("PluginsWindow_ClearLabel"), _globalizer.GetResourceString("PluginsWindow_ClearTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
try
{
PluginHelperInstance.DeleteAllPlugins();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("PluginsWindow_ClearErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ConfigPlugin_Click(object sender, RoutedEventArgs e)
{
try
{
var pluginItem = ((PluginItem)((Button)e.Source).DataContext);
PluginHelperInstance.OpenConfigForm(pluginItem.Plugin, this);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("PluginsWindow_ConfigErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void OpenPluginsFolder_Click(object sender, RoutedEventArgs e)
{
try
{
Process.Start("explorer.exe", PluginHelper.PluginFolder);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("PluginsWindow_OpenErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void PluginsForum_Click(object sender, RoutedEventArgs e)
{
Process.Start(Config.Default.ServerManagerPluginUrl);
}
private void RemovePlugin_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show(_globalizer.GetResourceString("PluginsWindow_DeleteLabel"), _globalizer.GetResourceString("PluginsWindow_DeleteTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
try
{
var pluginItem = ((PluginItem)((Button)e.Source).DataContext);
PluginHelperInstance.DeletePlugin(pluginItem.PluginFile);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("PluginsWindow_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}

View file

@ -0,0 +1,76 @@
<Window x:Class="ServerManagerTool.ProcessorAffinityWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:cctl="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:cvr="clr-namespace:ServerManagerTool.Common.ValidationRules;assembly=ServerManager.Common"
MinWidth="300" MinHeight="200" Width="300" Height="300" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" ResizeMode="CanResize"
Icon="../Art/favicon.ico" Title="{DynamicResource ProcessorAffinity_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:InvertBooleanConverter x:Key="InvertBooleanConverter"/>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Margin="5,5,5,0" Text="{DynamicResource ProcessorAffinity_InstructionLabel}" VerticalAlignment="Center" TextWrapping="Wrap" />
<CheckBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Margin="5,5,5,0" VerticalAlignment="Center" HorizontalAlignment="Left" Content="{DynamicResource ProcessorAffinity_AllCpuLabel}" IsChecked="{Binding ProcessorAffinityList.AllProcessors, Mode=TwoWay}" ToolTip="{DynamicResource ProcessorAffinity_AllCpuTooltip}"/>
<DataGrid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Margin="5" Name="ModDetailsGrid" ItemsSource="{Binding ProcessorAffinityList}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserSortColumns="False" RowHeaderWidth="0" SelectionMode="Single" IsEnabled="{Binding ProcessorAffinityList.AllProcessors, Converter={StaticResource InvertBooleanConverter}}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
</Style.Resources>
</Style>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridCheckBoxColumn Width="Auto" Binding="{Binding Selected, Mode=TwoWay}">
<DataGridCheckBoxColumn.Header>
<TextBlock Text="{DynamicResource ProcessorAffinity_SelectedColumnLabel}" />
</DataGridCheckBoxColumn.Header>
</DataGridCheckBoxColumn>
<DataGridTextColumn Width="*" Binding="{Binding Description, Mode=OneWay}">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource ProcessorAffinity_DescriptionColumnLabel}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="3" Grid.Column="0" Content="{DynamicResource ProcessorAffinity_SelectAllButtonLabel}" Margin="5" MinWidth="40" HorizontalAlignment="Right" Click="SelectAll_Click" IsEnabled="{Binding ProcessorAffinityList.AllProcessors, Converter={StaticResource InvertBooleanConverter}}"/>
<Button Grid.Row="3" Grid.Column="1" Content="{DynamicResource ProcessorAffinity_UnselectAllButtonLabel}" Margin="5" MinWidth="40" HorizontalAlignment="Right" Click="UnselectAll_Click" IsEnabled="{Binding ProcessorAffinityList.AllProcessors, Converter={StaticResource InvertBooleanConverter}}"/>
<Button Grid.Row="3" Grid.Column="2" Content="{DynamicResource ProcessorAffinity_ProcessButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Right" Click="Process_Click"/>
<Button Grid.Row="3" Grid.Column="3" Content="{DynamicResource ProcessorAffinity_CancelButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" IsCancel="True"/>
</Grid>
</Window>

View file

@ -0,0 +1,66 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using System.Numerics;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for ProcessorAffinityWindow.xaml
/// </summary>
public partial class ProcessorAffinityWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty ProcessorAffinityListProperty = DependencyProperty.Register(nameof(ProcessorAffinityList), typeof(ProcessorAffinityList), typeof(ProcessorAffinityWindow), new PropertyMetadata(null));
public ProcessorAffinityList ProcessorAffinityList
{
get { return (ProcessorAffinityList)GetValue(ProcessorAffinityListProperty); }
set { SetValue(ProcessorAffinityListProperty, value); }
}
public ProcessorAffinityWindow(string profileName, BigInteger affinityValue)
{
AffinityValue = affinityValue;
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.Title = string.Format(_globalizer.GetResourceString("ProcessorAffinity_ProfileTitle"), profileName);
this.ProcessorAffinityList = new ProcessorAffinityList(affinityValue);
this.DataContext = this;
}
public BigInteger AffinityValue
{
get;
set;
}
private void Process_Click(object sender, RoutedEventArgs e)
{
AffinityValue = this.ProcessorAffinityList.AffinityValue;
DialogResult = true;
Close();
}
private void SelectAll_Click(object sender, RoutedEventArgs e)
{
foreach (var item in ProcessorAffinityList)
{
item.Selected = true;
}
}
private void UnselectAll_Click(object sender, RoutedEventArgs e)
{
foreach (var item in ProcessorAffinityList)
{
item.Selected = false;
}
}
}
}

View file

@ -0,0 +1,124 @@
<Window x:Class="ServerManagerTool.ProfileSyncWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinWidth="400" MinHeight="640" Width="640" Height="500" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" ResizeMode="CanResize"
Loaded="Window_Loaded"
Icon="../Art/favicon.ico" Title="{DynamicResource ProfileSyncWindow_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5,5,5,0" Text="{DynamicResource ProfileSyncWindow_SyncInformationLabel}" TextWrapping="Wrap" VerticalAlignment="Center" FontWeight="Bold"/>
<ListView Grid.Row="2" Grid.Column="0" Margin="5,5,5,0" ItemsSource="{Binding SyncSections}" HorizontalContentAlignment="Stretch">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto">
<GridViewColumnHeader>
<TextBlock Text="{DynamicResource ProfileSyncWindow_SelectedColumnLabel}" />
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Selected}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="330" DisplayMemberBinding="{Binding SectionName}">
<GridViewColumnHeader HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" Padding="5,0,0,0">
<TextBlock Text="{DynamicResource ProfileSyncWindow_SectionNameColumnLabel}" />
</GridViewColumnHeader>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="White" />
<Setter Property="Height" Value="20" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ListView Grid.Row="2" Grid.Column="1" Margin="5,5,5,0" ItemsSource="{Binding SyncProfiles}" HorizontalContentAlignment="Stretch">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto">
<GridViewColumnHeader>
<TextBlock Text="{DynamicResource ProfileSyncWindow_SelectedColumnLabel}" />
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Selected}" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="330" DisplayMemberBinding="{Binding ProfileName}">
<GridViewColumnHeader HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" Padding="5,0,0,0">
<TextBlock Text="{DynamicResource ProfileSyncWindow_ProfileNameColumnLabel}" />
</GridViewColumnHeader>
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="White" />
<Setter Property="Height" Value="20" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<DockPanel Grid.Row="3" Grid.Column="0">
<Button DockPanel.Dock="Left" Content="{DynamicResource ProfileSyncWindow_SelectAllButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" Click="SectionSelectAll_Click"/>
<Button DockPanel.Dock="Left" Content="{DynamicResource ProfileSyncWindow_UnselectAllButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" Click="SectionUnselectAll_Click"/>
</DockPanel>
<DockPanel Grid.Row="3" Grid.Column="1">
<Button DockPanel.Dock="Left" Content="{DynamicResource ProfileSyncWindow_SelectAllButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" Click="ProfileSelectAll_Click"/>
<Button DockPanel.Dock="Left" Content="{DynamicResource ProfileSyncWindow_UnselectAllButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Left" Click="ProfileUnselectAll_Click"/>
</DockPanel>
<DockPanel Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2">
<Button DockPanel.Dock="Right" Content="{DynamicResource ProfileSyncWindow_CloseButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Right" IsCancel="True"/>
<Button DockPanel.Dock="Right" Content="{DynamicResource ProfileSyncWindow_ProcessButtonLabel}" Margin="5" MinWidth="75" HorizontalAlignment="Right" Click="Process_Click"/>
</DockPanel>
</Grid>
<!-- 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 Background="Black" Opacity="0.5"/>
<Border MinWidth="250" Background="Orange" BorderBrush="Black" BorderThickness="1" CornerRadius="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="OverlayProfile" Grid.Row="0" Margin="5,0,5,1" FontWeight="Bold" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" />
<Label x:Name="OverlaySection" Grid.Row="1" Margin="5,1,5,0" FontWeight="Bold" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" />
</Grid>
</Border>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,250 @@
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for ProfileSyncWindow.xaml
/// </summary>
public partial class ProfileSyncWindow : Window
{
public class SyncProfile : DependencyObject
{
public static readonly DependencyProperty SelectedProperty = DependencyProperty.Register(nameof(Selected), typeof(bool), typeof(SyncProfile), new PropertyMetadata(false));
public static readonly DependencyProperty ProfileNameProperty = DependencyProperty.Register(nameof(ProfileName), typeof(string), typeof(SyncProfile), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty ProfileProperty = DependencyProperty.Register(nameof(Profile), typeof(ServerProfile), typeof(SyncProfile), new PropertyMetadata(null));
public bool Selected
{
get { return (bool)GetValue(SelectedProperty); }
set { SetValue(SelectedProperty, value); }
}
public string ProfileName
{
get { return (string)GetValue(ProfileNameProperty); }
set { SetValue(ProfileNameProperty, value); }
}
public ServerProfile Profile
{
get { return (ServerProfile)GetValue(ProfileProperty); }
set { SetValue(ProfileProperty, value); }
}
}
public class SyncSection : DependencyObject
{
public static readonly DependencyProperty SelectedProperty = DependencyProperty.Register(nameof(Selected), typeof(bool), typeof(SyncSection), new PropertyMetadata(false));
public static readonly DependencyProperty SectionNameProperty = DependencyProperty.Register(nameof(SectionName), typeof(string), typeof(SyncSection), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty CategoryProperty = DependencyProperty.Register(nameof(Category), typeof(ServerProfileCategory), typeof(SyncSection));
public bool Selected
{
get { return (bool)GetValue(SelectedProperty); }
set { SetValue(SelectedProperty, value); }
}
public string SectionName
{
get { return (string)GetValue(SectionNameProperty); }
set { SetValue(SectionNameProperty, value); }
}
public ServerProfileCategory Category
{
get { return (ServerProfileCategory)GetValue(CategoryProperty); }
set { SetValue(CategoryProperty, value); }
}
}
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty SyncProfilesProperty = DependencyProperty.Register(nameof(SyncProfiles), typeof(ObservableCollection<SyncProfile>), typeof(ProfileSyncWindow), new PropertyMetadata(new ObservableCollection<SyncProfile>()));
public static readonly DependencyProperty SyncSectionsProperty = DependencyProperty.Register(nameof(SyncSections), typeof(ObservableCollection<SyncSection>), typeof(ProfileSyncWindow), new PropertyMetadata(new ObservableCollection<SyncSection>()));
public ProfileSyncWindow(ServerManager serverManager, ServerProfile serverProfile)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
OverlayGrid.Visibility = Visibility.Collapsed;
this.Title = string.Format(_globalizer.GetResourceString("ProfileSyncWindow_ProfileTitle"), serverProfile?.ProfileName);
this.ServerManager = serverManager;
this.ServerProfile = serverProfile;
this.DataContext = this;
}
public ServerManager ServerManager
{
get;
private set;
}
public ServerProfile ServerProfile
{
get;
private set;
}
public ObservableCollection<SyncProfile> SyncProfiles
{
get { return (ObservableCollection<SyncProfile>)GetValue(SyncProfilesProperty); }
set { SetValue(SyncProfilesProperty, value); }
}
public ObservableCollection<SyncSection> SyncSections
{
get { return (ObservableCollection<SyncSection>)GetValue(SyncSectionsProperty); }
set { SetValue(SyncSectionsProperty, value); }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
CreateSyncProfileList();
CreateSyncSectionList();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ProfileSyncWindow_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async void Process_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
if (!SyncProfiles.Any(s => s.Selected))
{
MessageBox.Show(_globalizer.GetResourceString("ProfileSyncWindow_Process_NoProfilesSelectedLabel"), _globalizer.GetResourceString("ProfileSyncWindow_Process_Title"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
if (!SyncSections.Any(s => s.Selected))
{
MessageBox.Show(_globalizer.GetResourceString("ProfileSyncWindow_Process_NoSectionsSelectedLabel"), _globalizer.GetResourceString("ProfileSyncWindow_Process_Title"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
if (MessageBox.Show(_globalizer.GetResourceString("ProfileSyncWindow_Process_ConfirmLabel"), _globalizer.GetResourceString("ProfileSyncWindow_Process_Title"), MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
return;
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
OverlayProfile.Content = string.Empty;
OverlaySection.Content = string.Empty;
OverlayGrid.Visibility = Visibility.Visible;
await Task.Delay(100);
await PerformProfileSync();
MessageBox.Show(_globalizer.GetResourceString("ProfileSyncWindow_Process_SuccessLabel"), _globalizer.GetResourceString("ProfileSyncWindow_Process_Title"), MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ProfileSyncWindow_Process_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
OverlayGrid.Visibility = Visibility.Collapsed;
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private void ProfileSelectAll_Click(object sender, RoutedEventArgs e)
{
foreach (var profile in SyncProfiles)
{
profile.Selected = true;
}
}
private void ProfileUnselectAll_Click(object sender, RoutedEventArgs e)
{
foreach (var profile in SyncProfiles)
{
profile.Selected = false;
}
}
private void SectionSelectAll_Click(object sender, RoutedEventArgs e)
{
foreach (var section in SyncSections)
{
section.Selected = true;
}
}
private void SectionUnselectAll_Click(object sender, RoutedEventArgs e)
{
foreach (var section in SyncSections)
{
section.Selected = false;
}
}
private void CreateSyncProfileList()
{
SyncProfiles.Clear();
if (this.ServerManager == null || this.ServerManager.Servers == null)
return;
foreach (var server in this.ServerManager.Servers)
{
if (server.Profile == ServerProfile)
continue;
SyncProfiles.Add(new SyncProfile() { Selected = false, Profile = server.Profile, ProfileName = server.Profile.ProfileName });
}
}
private void CreateSyncSectionList()
{
SyncSections.Clear();
SyncSections.Add(new SyncSection() { Selected = false, Category = ServerProfileCategory.Administration, SectionName = _globalizer.GetResourceString("ServerSettings_AdministrationSectionLabel") });
SyncSections.Add(new SyncSection() { Selected = false, Category = ServerProfileCategory.AutomaticManagement, SectionName = _globalizer.GetResourceString("ServerSettings_AutomaticManagementLabel") });
SyncSections.Add(new SyncSection() { Selected = false, Category = ServerProfileCategory.ServerFiles, SectionName = _globalizer.GetResourceString("ServerSettings_ServerFilesLabel") });
}
private async Task PerformProfileSync()
{
foreach (var syncProfile in SyncProfiles)
{
if (!syncProfile.Selected)
continue;
OverlayProfile.Content = syncProfile.ProfileName ?? string.Empty;
bool updateSchedules = false;
foreach (var syncSection in SyncSections)
{
if (!syncSection.Selected)
continue;
OverlaySection.Content = syncSection.SectionName;
await Task.Delay(100);
syncProfile.Profile?.SyncSettings(syncSection.Category, ServerProfile);
if (syncSection.Category == ServerProfileCategory.AutomaticManagement)
updateSchedules = true;
}
syncProfile.Profile?.Save(false, updateSchedules, null);
}
}
}
}

View file

@ -0,0 +1,28 @@
<Window x:Class="ServerManagerTool.ProgressWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640" Height="480" ResizeMode="CanResize" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Closing="Window_Closing"
Icon="../Art/favicon.ico" Title="{DynamicResource Progress_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition Height="400*"/>
</Grid.RowDefinitions>
<TextBox Name="MessageOutput" Grid.Row="0" HorizontalAlignment="Stretch" IsReadOnly="True" IsReadOnlyCaretVisible="True" TextWrapping="NoWrap" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="5"/>
</Grid>
</Window>

View file

@ -0,0 +1,53 @@
using ServerManagerTool.Common.Utils;
using System;
using System.Diagnostics;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for ProgressWindow.xaml
/// </summary>
public partial class ProgressWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private bool _allowClose = false;
public ProgressWindow(string windowTitle)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
if (!string.IsNullOrWhiteSpace(windowTitle))
Title = windowTitle;
Application.Current.Dispatcher.Invoke(() => MessageOutput.Cursor = System.Windows.Input.Cursors.Wait);
_allowClose = false;
this.DataContext = this;
}
public void AddMessage(string message, bool includeNewLine = true)
{
MessageOutput.AppendText(message);
if (includeNewLine)
MessageOutput.AppendText(Environment.NewLine);
MessageOutput.ScrollToEnd();
Debug.WriteLine(message);
}
public void CloseWindow()
{
Application.Current.Dispatcher.Invoke(() => MessageOutput.Cursor = null);
_allowClose = true;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!_allowClose)
e.Cancel = true;
}
}
}

View file

@ -0,0 +1,284 @@
<Window x:Class="ServerManagerTool.RconWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:sm="clr-namespace:ServerManagerTool"
xmlns:en="clr-namespace:ServerManagerTool.Enums"
xmlns:vm="clr-namespace:ServerManagerTool.Lib.ViewModel"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
Width="1024" Height="768" MinWidth="640" MinHeight="480" ResizeMode="CanResizeWithGrip"
SizeChanged="RCON_SizeChanged" LocationChanged="RCON_LocationChanged"
Name="RCON" Icon="../Art/favicon.ico" Title="{Binding RconParameters.WindowTitle}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sm:ScrollToBottomAction x:Key="ScrollToBottomAction" />
<cc:EnumConverter x:Key="EnumConverter"/>
<cc:EnumFlagsConverter x:Key="EnumFlagsConverter"/>
<cc:IsNullOrWhiteSpaceValueConverter x:Key="IsNullOrWhiteSpaceValueConverter"/>
<cc:IsNullValueConverter x:Key="IsNullValueConverter"/>
<cc:NullValueConverter x:Key="NullValueConverter"/>
<SolidColorBrush x:Key="HeaderBrush" Color="#FFF0F0F0"/>
<Style x:Key="PlayerStatus" TargetType="Border">
<Setter Property="BorderBrush" Value="Gray"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsOnline}" Value="True">
<Setter Property="BorderBrush" Value="LightGreen"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsOnline}" Value="False" />
<Condition Binding="{Binding IsValid}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Orange"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsOnline}" Value="False" />
<Condition Binding="{Binding IsWhitelisted}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="#0066CC"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="GuildName" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding GuildName, Converter={StaticResource IsNullOrWhiteSpaceValueConverter}}" Value="True">
<Setter Property="Content" Value="No clan"/>
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="Foreground" Value="LightGray"/>
</DataTrigger>
<DataTrigger Binding="{Binding GuildName, Converter={StaticResource IsNullOrWhiteSpaceValueConverter}}" Value="False">
<Setter Property="Content" Value="{Binding PlayerData.Guild.GuildName}"/>
<Setter Property="FontStyle" Value="Normal"/>
<Setter Property="Foreground" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="InputModes">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="sm:InputMode" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid x:Name="dockPanel">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="200" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" MinWidth="200" x:Name="playerListColumn" />
</Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" BorderThickness="1" BorderBrush="LightGray" Margin="2" Background="{StaticResource HeaderBrush}">
<DockPanel Margin="2,2,2,1" LastChildFill="False">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<Menu VerticalAlignment="Center" Margin="1" Background="{StaticResource HeaderBrush}">
<MenuItem Header="{DynamicResource RCON_Menu_File}" VerticalAlignment="Stretch">
<MenuItem Header="{DynamicResource RCON_Menu_ViewLogs}" Command="{Binding ViewLogsCommand}"/>
<MenuItem Header="{DynamicResource RCON_Menu_ClearLogs}">
<MenuItem Header="{DynamicResource RCON_Menu_Confirm}" Command="{Binding ClearLogsCommand}"/>
</MenuItem>
</MenuItem>
</Menu>
<Menu VerticalAlignment="Center" Margin="1" Background="{StaticResource HeaderBrush}">
<MenuItem Header="{DynamicResource RCON_Menu_Console}" VerticalAlignment="Stretch">
<MenuItem Header="{DynamicResource RCON_Menu_AutoScroll}" IsCheckable="True" IsChecked="{Binding ScrollOnNewInput}"/>
</MenuItem>
</Menu>
<Menu VerticalAlignment="Center" Margin="1" Background="{StaticResource HeaderBrush}">
<MenuItem Header="{DynamicResource RCON_Menu_Players}">
<MenuItem Header="{DynamicResource RCON_Menu_Sort}">
<MenuItem Header="{DynamicResource RCON_Menu_SortOnline}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.Online}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=Online}"/>
<MenuItem Header="{DynamicResource RCON_Menu_SortName}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.Name}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=Name}"/>
<MenuItem Header="{DynamicResource RCON_Menu_SortTribe}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.Tribe}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=Tribe}"/>
<MenuItem Header="{DynamicResource RCON_Menu_SortLastOnline}" IsCheckable="True" Command="{Binding SortPlayersCommand}" CommandParameter="{x:Static en:PlayerSortType.LastOnline}" IsChecked="{Binding PlayerSorting, Converter={StaticResource EnumConverter}, ConverterParameter=LastOnline}"/>
</MenuItem>
<MenuItem Header="{DynamicResource RCON_Menu_Filter}">
<MenuItem Header="{DynamicResource RCON_Menu_FilterOnline}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Online}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Online, Mode=OneWay}"/>
<MenuItem Header="{DynamicResource RCON_Menu_FilterOffline}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Offline}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Offline, Mode=OneWay}"/>
<MenuItem Header="{DynamicResource RCON_Menu_FilterWhitelisted}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Whitelisted}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Whitelisted, Mode=OneWay}"/>
<MenuItem Header="{DynamicResource RCON_Menu_FilterInvalid}" IsCheckable="True" Command="{Binding FilterPlayersCommand}" CommandParameter="{x:Static en:PlayerFilterType.Invalid}" IsChecked="{Binding PlayerFiltering, Converter={StaticResource EnumFlagsConverter}, ConverterParameter=Invalid, Mode=OneWay}"/>
</MenuItem>
</MenuItem>
</Menu>
</StackPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Right">
<Label Content="{DynamicResource RCON_PlayersLabel}" Margin="5,0,0,0"/>
<Label Content="{Binding ServerRcon.CountOnlinePlayers, FallbackValue=0}"/>
<Label Content="{DynamicResource RCON_PlayersSeparatorLabel}" Name="PlayerCountSeparator"/>
<Label Content="{Binding RconParameters.MaxPlayers, FallbackValue=0}" Name="MaxPlayerLabel"/>
</StackPanel>
</DockPanel>
</Border>
<RichTextBox Grid.Row="1" Grid.Column="0" BorderBrush="LightGray" VerticalAlignment="Stretch" VerticalScrollBarVisibility="Visible" Margin="2,1,0,1" HorizontalScrollBarVisibility="Auto" IsReadOnlyCaretVisible="True" IsReadOnly="True" IsTabStop="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged" >
<sm:ScrollToBottomAction IsEnabled="{Binding ScrollOnNewInput}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_Command}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_NoResponse}">
<Setter Property="FontStyle" Value="Italic"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_CommandOutput}">
<Setter Property="FontStyle" Value="Normal"/>
<Setter Property="FontWeight" Value="Normal"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_CommandTime}">
<Setter Property="FontStyle" Value="Normal"/>
<Setter Property="FontWeight" Value="Normal"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_ConnectionChanged}">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="FontWeight" Value="Normal"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_Broadcast}">
<Setter Property="FontStyle" Value="Normal"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style TargetType="{x:Type sm:RconOutput_Comment}">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="FontWeight" Value="Normal"/>
</Style>
</RichTextBox.Resources>
<FlowDocument Name="ConsoleContent"/>
</RichTextBox>
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" ShowsPreview="True" HorizontalAlignment="Center" VerticalAlignment="Stretch" Opacity="0"/>
<DockPanel x:Name="playerListPanel" Grid.Row="1" Grid.Column="2" Margin="0,1,2,1" DataContext="{Binding}" SizeChanged="PlayerList_SizeChanged">
<DockPanel DockPanel.Dock="Top">
<Label DockPanel.Dock="Left" Content="{DynamicResource General_FilterLabel}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button DockPanel.Dock="Right" Margin="5,0,5,0" Width="22" Height="22" HorizontalAlignment="Right" VerticalAlignment="Center" Click="FilterPlayerList_Click" ToolTip="{DynamicResource General_FilterButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Filter.ico,Size=32}"/>
</Button>
<TextBox DockPanel.Dock="Left" Text="{Binding PlayerFilterString, Mode=TwoWay}" BorderBrush="LightGray" VerticalAlignment="Center" VerticalContentAlignment="Center" IsTabStop="True" Margin="0" Padding="2"/>
</DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Label Content="{DynamicResource RCON_ProfileCountLabel}" HorizontalAlignment="Center"/>
<Label Content="{Binding ServerRcon.CountPlayers, FallbackValue=0}" HorizontalAlignment="Center"/>
<Label Content="{DynamicResource RCON_ProfileInvalidCountLabel}" HorizontalAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ServerRcon.CountInvalidPlayers, FallbackValue=0, Converter={cc:GreaterThanIntValueConverter 0}}" Value="true">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Label Content="{Binding ServerRcon.CountInvalidPlayers, FallbackValue=0}" HorizontalAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource {x:Type Label}}" TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ServerRcon.CountInvalidPlayers, FallbackValue=0, Converter={cc:GreaterThanIntValueConverter 0}}" Value="true">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackPanel>
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding ServerRcon.Players}" BorderBrush="LightGray" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListBox.Resources>
<clib:BindingProxy x:Key="proxy" Data="{Binding ElementName=RCON}"/>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{DynamicResource RCON_Menu_ViewProfile}" Command="{Binding Source={StaticResource proxy}, Path=Data.ViewPlayerProfileCommand}" CommandParameter="{Binding}" />
<MenuItem Header="{DynamicResource RCON_Menu_ViewTribe}" Command="{Binding Source={StaticResource proxy}, Path=Data.ViewPlayerTribeCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:PlayerInfo}">
<Border BorderThickness="2" Padding="1" Style="{DynamicResource PlayerStatus}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" HorizontalAlignment="Stretch" ToolTip="{Binding PlayerId}">
<StackPanel Orientation="Horizontal" Margin="0" >
<Label Content="{Binding CharacterName}" Padding="0,-1,0,-1"/>
<TextBlock Text="{Binding PlayerData.Level, StringFormat=({0})}" Margin="5,0,0,0" Padding="0,-1,0,-1" />
</StackPanel>
<Label Padding="0,-1,0,-1" Style="{DynamicResource GuildName}"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Margin="2,1,2,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{DynamicResource RCON_ModeLabel}"/>
<ComboBox Name="InputModesComboBox" Grid.Column="1" ItemsSource="{Binding Source={StaticResource InputModes}}" SelectedItem="{Binding CurrentInputMode, Mode=TwoWay}" Margin="0,0,2,0"/>
<TextBox Name="ConsoleInput" TabIndex="0" Grid.Column="2" KeyUp="ConsoleInput_KeyUp" BorderBrush="LightGray" VerticalContentAlignment="Center" IsTabStop="True"/>
</Grid>
</Grid>
<!-- 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="inputBox" 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>
<TextBlock x:Name="inputTitle" Margin="5" FontWeight="Bold" />
<TextBox x:Name="inputTextBox" MinWidth="150" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,0,5,0" MaxLength="200"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="button1" Margin="5" Content="{DynamicResource RCON_Button_OK}" Background="{x:Null}" Command="{Binding Button1Command}" CommandParameter="{Binding}"/>
<Button x:Name="button2" Margin="5" Content="{DynamicResource RCON_Button_Cancel}" Background="{x:Null}" Command="{Binding Button2Command}" CommandParameter="{Binding}"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,896 @@
using ServerManagerTool.Common.Enums;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Lib.ViewModel;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum InputMode
{
Command,
//Global,
Broadcast,
}
public enum InputWindowMode
{
None,
ServerChatTo,
RenamePlayer,
RenameTribe,
}
public class ScrollToBottomAction : TriggerAction<RichTextBox>
{
protected override void Invoke(object parameter)
{
AssociatedObject.ScrollToEnd();
}
}
public class RconOutput_CommandTime : Run
{
public RconOutput_CommandTime()
: this(DateTime.Now)
{
}
public RconOutput_CommandTime(DateTime time)
: base($"[{time.ToString("g")}] ")
{
}
}
public class RconOutput_TimedCommand : Span
{
protected RconOutput_TimedCommand()
: base()
{
base.Inlines.Add(new RconOutput_CommandTime());
}
public RconOutput_TimedCommand(Inline output)
: this()
{
base.Inlines.Add(output);
}
public RconOutput_TimedCommand(string output)
: this(new Run(output))
{
}
}
public class RconOutput_Comment : Run
{
public RconOutput_Comment(string value)
: base(value)
{
Foreground = Brushes.Green;
}
}
public class RconOutput_ChatSend : RconOutput_TimedCommand
{
public RconOutput_ChatSend(string target, string output)
: base($"[{target}] {output}")
{
}
}
public class RconOutput_Broadcast : RconOutput_ChatSend
{
public RconOutput_Broadcast(string output)
: base("ALL", output)
{
Foreground = Brushes.Orange;
}
}
public class RconOutput_ConnectionChanged : RconOutput_TimedCommand
{
public RconOutput_ConnectionChanged(bool isConnected)
: base(isConnected ? GlobalizedApplication.Instance.GetResourceString("RCON_ConnectionEstablishedLabel") : GlobalizedApplication.Instance.GetResourceString("RCON_ConnectionLostLabel"))
{
Foreground = Brushes.Orange;
}
}
public class RconOutput_Command : RconOutput_TimedCommand
{
public RconOutput_Command(string text)
: base(text)
{
}
};
public class RconOutput_NoResponse : RconOutput_TimedCommand
{
public RconOutput_NoResponse()
: base(GlobalizedApplication.Instance.GetResourceString("RCON_NoCommandResponseLabel"))
{
Foreground = Brushes.LightGray;
}
};
public class RconOutput_CommandOutput : RconOutput_TimedCommand
{
public RconOutput_CommandOutput(string text)
: base(text)
{
}
};
public class RconOutput_PlayerJoined : RconOutput_TimedCommand
{
public RconOutput_PlayerJoined(string text)
: base(text)
{
Foreground = new SolidColorBrush(Color.FromArgb(255, 85, 142, 181));
}
};
public class RconOutput_PlayerLeft : RconOutput_TimedCommand
{
public RconOutput_PlayerLeft(string text)
: base(text)
{
Foreground = new SolidColorBrush(Color.FromArgb(255, 62, 104, 132));
}
};
/// <summary>
/// Interaction logic for RconWindow.xaml
/// </summary>
public partial class RconWindow : Window
{
private static Dictionary<Server, RconWindow> RconWindows = new Dictionary<Server, RconWindow>();
private GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty ConfigProperty = DependencyProperty.Register(nameof(Config), typeof(Config), typeof(RconWindow), new PropertyMetadata(Config.Default));
public static readonly DependencyProperty CurrentInputModeProperty = DependencyProperty.Register(nameof(CurrentInputMode), typeof(InputMode), typeof(RconWindow), new PropertyMetadata(InputMode.Command));
public static readonly DependencyProperty PlayerFilteringProperty = DependencyProperty.Register(nameof(PlayerFiltering), typeof(PlayerFilterType), typeof(RconWindow), new PropertyMetadata(PlayerFilterType.Online | PlayerFilterType.Offline));
public static readonly DependencyProperty PlayerFilterStringProperty = DependencyProperty.Register(nameof(PlayerFilterString), typeof(string), typeof(RconWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty PlayerSortingProperty = DependencyProperty.Register(nameof(PlayerSorting), typeof(PlayerSortType), typeof(RconWindow), new PropertyMetadata(PlayerSortType.Online));
public static readonly DependencyProperty PlayersViewProperty = DependencyProperty.Register(nameof(PlayersView), typeof(ICollectionView), typeof(RconWindow), new PropertyMetadata(null));
public static readonly DependencyProperty RconParametersProperty = DependencyProperty.Register(nameof(RconParameters), typeof(RconParameters), typeof(RconWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ScrollOnNewInputProperty = DependencyProperty.Register(nameof(ScrollOnNewInput), typeof(bool), typeof(RconWindow), new PropertyMetadata(true));
public static readonly DependencyProperty ServerRconProperty = DependencyProperty.Register(nameof(ServerRcon), typeof(ServerRcon), typeof(RconWindow), new PropertyMetadata(null));
public RconWindow(RconParameters parameters)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.CurrentInputWindowMode = InputWindowMode.None;
this.PlayerFiltering = (PlayerFilterType)Config.Default.RCON_PlayerListFilter;
this.PlayerSorting = (PlayerSortType)Config.Default.RCON_PlayerListSort;
this.RconParameters = parameters;
this.ServerRcon = new ServerRcon(parameters);
this.ServerRcon.RegisterCommandListener(RenderRCONCommandOutput);
this.ServerRcon.Players.CollectionChanged += Players_CollectionChanged;
this.ServerRcon.PlayersCollectionUpdated += Players_CollectionUpdated;
this.PlayersView = CollectionViewSource.GetDefaultView(this.ServerRcon.Players);
this.PlayersView.Filter = new Predicate<object>(PlayerFilter);
if (this.RconParameters?.Server?.Runtime != null)
{
this.RconParameters.Server.Runtime.StatusUpdate += Runtime_StatusUpdate;
}
if (this.RconParameters?.Server == null)
{
this.PlayerCountSeparator.Visibility = Visibility.Collapsed;
this.MaxPlayerLabel.Visibility = Visibility.Collapsed;
}
this.DataContext = this;
RenderCommentsBlock(
_globalizer.GetResourceString("RCON_Comments_Line1"),
_globalizer.GetResourceString("RCON_Comments_Line2"),
_globalizer.GetResourceString("RCON_Comments_Line3"),
_globalizer.GetResourceString("RCON_Comments_Line4"),
_globalizer.GetResourceString("RCON_Comments_Line5"),
String.Format(_globalizer.GetResourceString("RCON_Comments_Line6"), _globalizer.GetResourceString("RCON_Help_Keyword"))
);
if (this.RconParameters.WindowExtents.Width > 50 && this.RconParameters.WindowExtents.Height > 50)
{
this.Left = this.RconParameters.WindowExtents.Left;
this.Top = this.RconParameters.WindowExtents.Top;
this.Width = this.RconParameters.WindowExtents.Width;
this.Height = this.RconParameters.WindowExtents.Height;
//
// Fix issues where the console was saved while offscreen.
if(this.Left == -32000)
{
this.Left = 0;
}
if(this.Top == -32000)
{
this.Top = 0;
}
}
SetPlayerListWidth(this.RconParameters.PlayerListWidth);
this.ConsoleInput.Focus();
// hook into the language change event
GlobalizedApplication.Instance.GlobalizationManager.ResourceDictionaryChangedEvent += ResourceDictionaryChangedEvent;
}
#region Properties
public Config Config
{
get { return (Config)GetValue(ConfigProperty); }
set { SetValue(ConfigProperty, value); }
}
public InputMode CurrentInputMode
{
get { return (InputMode)GetValue(CurrentInputModeProperty); }
set { SetValue(CurrentInputModeProperty, value); }
}
public PlayerFilterType PlayerFiltering
{
get { return (PlayerFilterType)GetValue(PlayerFilteringProperty); }
set { SetValue(PlayerFilteringProperty, value); }
}
public string PlayerFilterString
{
get { return (string)GetValue(PlayerFilterStringProperty); }
set { SetValue(PlayerFilterStringProperty, value); }
}
public PlayerSortType PlayerSorting
{
get { return (PlayerSortType)GetValue(PlayerSortingProperty); }
set { SetValue(PlayerSortingProperty, value); }
}
public ICollectionView PlayersView
{
get { return (ICollectionView)GetValue(PlayersViewProperty); }
set { SetValue(PlayersViewProperty, value); }
}
public RconParameters RconParameters
{
get { return (RconParameters)GetValue(RconParametersProperty); }
set { SetValue(RconParametersProperty, value); }
}
public bool ScrollOnNewInput
{
get { return (bool)GetValue(ScrollOnNewInputProperty); }
set { SetValue(ScrollOnNewInputProperty, value); }
}
public ServerRcon ServerRcon
{
get { return (ServerRcon)GetValue(ServerRconProperty); }
set { SetValue(ServerRconProperty, value); }
}
#endregion
#region Commands
private InputWindowMode CurrentInputWindowMode
{
get;
set;
}
public ICommand Button1Command
{
get
{
return new RelayCommand<object>(
execute: (_) =>
{
inputBox.Visibility = Visibility.Collapsed;
dockPanel.IsEnabled = true;
PlayerInfo player;
var inputText = inputTextBox.Text;
switch (this.CurrentInputWindowMode)
{
default:
break;
}
// Clear InputBox.
inputTextBox.Text = String.Empty;
this.CurrentInputWindowMode = InputWindowMode.None;
},
canExecute: (_) => true
);
}
}
public ICommand Button2Command
{
get
{
return new RelayCommand<object>(
execute: (_) =>
{
inputBox.Visibility = Visibility.Collapsed;
dockPanel.IsEnabled = true;
switch (this.CurrentInputWindowMode)
{
default:
break;
}
// Clear InputBox.
inputTextBox.Text = String.Empty;
this.CurrentInputWindowMode = InputWindowMode.None;
},
canExecute: (_) => true
);
}
}
public ICommand ClearLogsCommand
{
get
{
return new RelayCommand<object>(
execute: (_) =>
{
string logsDir = String.Empty;
try
{
logsDir = App.GetProfileLogFolder(this.RconParameters.ProfileId);
Directory.Delete(logsDir, true);
}
catch (Exception)
{
// Ignore any failures here, best effort only.
}
MessageBox.Show(String.Format(_globalizer.GetResourceString("RCON_ClearLogs_Label"), logsDir), _globalizer.GetResourceString("RCON_ClearLogs_Title"), MessageBoxButton.OK, MessageBoxImage.Information);
},
canExecute: (_) => this.RconParameters.Server != null
);
}
}
public ICommand ViewLogsCommand
{
get
{
return new RelayCommand<object>(
execute: (_) =>
{
string logsDir = String.Empty;
try
{
logsDir = App.GetProfileLogFolder(this.RconParameters.ProfileId);
Process.Start(logsDir);
}
catch(Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("RCON_ViewLogs_ErrorLabel"), logsDir, ex.Message), _globalizer.GetResourceString("RCON_ViewLogs_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
},
canExecute: (_) => this.RconParameters.Server != null
);
}
}
public ICommand SortPlayersCommand
{
get
{
return new RelayCommand<PlayerSortType>(
execute: (sort) =>
{
Config.Default.RCON_PlayerListSort = (int)this.PlayerSorting;
SortPlayers();
},
canExecute: (sort) => true
);
}
}
public ICommand FilterPlayersCommand
{
get
{
return new RelayCommand<PlayerFilterType>(
execute: (filter) =>
{
this.PlayerFiltering ^= filter;
Config.Default.RCON_PlayerListFilter = (int)this.PlayerFiltering;
this.PlayersView.Refresh();
},
canExecute: (filter) => true
);
}
}
public ICommand ViewPlayerProfileCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) =>
{
var savedFilesPath = ServerProfile.GetProfileSavePath(this.RconParameters.InstallDirectory);
var window = new PlayerProfileWindow(player, savedFilesPath)
{
Owner = this
};
window.ShowDialog();
},
canExecute: (player) => player?.PlayerData != null
);
}
}
public ICommand ViewPlayerTribeCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) =>
{
var savedFilesPath = ServerProfile.GetProfileSavePath(this.RconParameters.InstallDirectory);
var window = new GuildProfileWindow(player, this.ServerRcon.Players, savedFilesPath)
{
Owner = this
};
window.ShowDialog();
},
canExecute: (player) => player?.PlayerData != null && !string.IsNullOrWhiteSpace(player.GuildName)
);
}
}
public ICommand CopyIDCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) =>
{
try
{
System.Windows.Clipboard.SetText(player.PlayerId.ToString());
MessageBox.Show($"{_globalizer.GetResourceString("RCON_CopyIdLabel")} {player.PlayerName}", _globalizer.GetResourceString("RCON_CopyIdTitle"), MessageBoxButton.OK);
}
catch
{
MessageBox.Show($"{_globalizer.GetResourceString("RCON_ClipboardErrorLabel")}", _globalizer.GetResourceString("RCON_ClipboardErrorTitle"), MessageBoxButton.OK);
}
},
canExecute: (player) => player != null
);
}
}
public ICommand CopyPlayerIDCommand
{
get
{
return new RelayCommand<PlayerInfo>(
execute: (player) =>
{
if (player.PlayerData != null)
{
try
{
System.Windows.Clipboard.SetText(player.PlayerData.CharacterId.ToString());
MessageBox.Show($"{_globalizer.GetResourceString("RCON_CopyPlayerIdLabel")} {player.CharacterName}", _globalizer.GetResourceString("RCON_CopyPlayerIdTitle"), MessageBoxButton.OK);
}
catch
{
MessageBox.Show($"{_globalizer.GetResourceString("RCON_ClipboardErrorLabel")}", _globalizer.GetResourceString("RCON_ClipboardErrorTitle"), MessageBoxButton.OK);
}
}
},
canExecute: (player) => player?.PlayerData != null && player.IsValid
);
}
}
#endregion
#region Events
public static RconWindow GetRconForServer(Server server)
{
if (!RconWindows.TryGetValue(server, out RconWindow window) || !window.IsLoaded)
{
window = new RconWindow(new RconParameters()
{
WindowTitle = String.Format(GlobalizedApplication.Instance.GetResourceString("RCON_TitleLabel"), server.Runtime.ProfileSnapshot.ProfileName),
WindowExtents = server.Profile.RconWindowExtents,
PlayerListWidth = server.Profile.RconPlayerListWidth,
Server = server,
InstallDirectory = server.Runtime.ProfileSnapshot.InstallDirectory,
GameFile = server.Runtime.ProfileSnapshot.GameFile,
ProfileId = server.Runtime.ProfileSnapshot.ProfileId,
ProfileName = server.Runtime.ProfileSnapshot.ProfileName,
MaxPlayers = server.Runtime.MaxPlayers,
RconHost = server.Runtime.ProfileSnapshot.ServerIP,
RconPort = server.Runtime.ProfileSnapshot.RconPort,
RconPassword = server.Runtime.ProfileSnapshot.RconPassword,
});
RconWindows[server] = window;
}
return window;
}
private void ResourceDictionaryChangedEvent(object source, ResourceDictionaryChangedEventArgs e)
{
// refresh the InputModes combobox list
this.InputModesComboBox.Items.Refresh();
// refresh the InputModes combobox value
var currentInputMode = CurrentInputMode;
this.InputModesComboBox.SelectedItem = null;
this.InputModesComboBox.SelectedItem = currentInputMode;
}
protected override void OnClosed(EventArgs e)
{
GlobalizedApplication.Instance.GlobalizationManager.ResourceDictionaryChangedEvent -= ResourceDictionaryChangedEvent;
if (this.RconParameters?.Server?.Runtime != null)
{
this.RconParameters.Server.Runtime.StatusUpdate -= Runtime_StatusUpdate;
}
base.OnClosed(e);
}
protected override void OnClosing(CancelEventArgs e)
{
this.ServerRcon.PlayersCollectionUpdated -= Players_CollectionUpdated;
this.ServerRcon.Players.CollectionChanged -= Players_CollectionChanged;
this.ServerRcon.DisposeAsync().DoNotWait();
if (this.RconParameters?.Server != null)
{
RconWindows.TryGetValue(this.RconParameters.Server, out RconWindow window);
if (window != null)
RconWindows.Remove(this.RconParameters.Server);
}
base.OnClosing(e);
}
private void RCON_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.WindowState == WindowState.Normal)
{
Rect savedRect = this.RconParameters.WindowExtents;
this.RconParameters.WindowExtents = new Rect(savedRect.Location, e.NewSize);
if (this.RconParameters.Server != null)
{
this.RconParameters.Server.Profile.RconWindowExtents = this.RconParameters.WindowExtents;
}
}
}
private void RCON_LocationChanged(object sender, EventArgs e)
{
if (this.WindowState != WindowState.Minimized && this.Left != -32000 && this.Top != -32000)
{
Rect savedRect = this.RconParameters.WindowExtents;
this.RconParameters.WindowExtents = new Rect(new Point(this.Left, this.Top), savedRect.Size);
if (this.RconParameters.Server != null)
{
this.RconParameters.Server.Profile.RconWindowExtents = this.RconParameters.WindowExtents;
}
}
}
private void ConsoleInput_KeyUp(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
var textBox = (TextBox)sender;
var effectiveMode = this.CurrentInputMode;
var commandText = textBox.Text.Trim();
if (commandText.StartsWith("/"))
{
effectiveMode = InputMode.Command;
commandText = commandText.Substring(1);
}
switch (effectiveMode)
{
case InputMode.Broadcast:
this.ServerRcon.IssueCommand($"{ServerRcon.RCON_COMMAND_BROADCAST} {commandText}");
break;
case InputMode.Command:
this.ServerRcon.IssueCommand(commandText);
break;
}
textBox.Text = String.Empty;
}
}
private void Players_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SortPlayers();
PlayersView?.Refresh();
}
private void Players_CollectionUpdated(object sender, EventArgs e)
{
this.PlayersView = CollectionViewSource.GetDefaultView(this.ServerRcon.Players);
this.PlayersView.Filter = new Predicate<object>(PlayerFilter);
SortPlayers();
PlayersView?.Refresh();
}
private void PlayerList_SizeChanged(object sender, SizeChangedEventArgs e)
{
this.RconParameters.PlayerListWidth = this.playerListColumn.Width.Value;
if (this.RconParameters.Server != null)
{
this.RconParameters.Server.Profile.RconPlayerListWidth = this.RconParameters.PlayerListWidth;
}
}
private void Runtime_StatusUpdate(object sender, EventArgs eventArgs)
{
this.RconParameters.ProfileName = this.RconParameters?.Server?.Runtime?.ProfileSnapshot.ProfileName ?? "<unknown>";
this.RconParameters.MaxPlayers = this.RconParameters?.Server?.Runtime?.MaxPlayers ?? 0;
}
#endregion
#region Methods
public static void CloseAllWindows()
{
var windows = RconWindows.Values.ToList();
foreach (var window in windows)
{
if (window.IsLoaded)
window.Close();
}
windows.Clear();
}
private void SetPlayerListWidth(double value)
{
if (value < this.playerListColumn.MinWidth)
this.playerListColumn.Width = new GridLength(this.playerListColumn.MinWidth, GridUnitType.Pixel);
else
this.playerListColumn.Width = new GridLength(value, GridUnitType.Pixel);
}
public void SortPlayers()
{
this.PlayersView.SortDescriptions.Clear();
switch (this.PlayerSorting)
{
case PlayerSortType.Name:
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
case PlayerSortType.Online:
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.IsOnline), ListSortDirection.Descending));
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
case PlayerSortType.Tribe:
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.GuildName), ListSortDirection.Ascending));
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
case PlayerSortType.LastOnline:
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.LastOnline), ListSortDirection.Descending));
this.PlayersView.SortDescriptions.Add(new SortDescription(nameof(PlayerInfo.PlayerName), ListSortDirection.Ascending));
break;
}
}
#endregion
#region Command Methods
private void AddBlockContent(Block b)
{
ConsoleContent.Blocks.Add(b);
}
private IEnumerable<Inline> FormatCommandInput(ConsoleCommand command)
{
if (command.command.Equals(ServerRcon.RCON_COMMAND_BROADCAST, StringComparison.OrdinalIgnoreCase))
{
yield return new RconOutput_Broadcast(command.args);
}
else
{
yield return new RconOutput_Command($"> {command.rawCommand}");
}
if (!command.suppressOutput && command.lines.Count() > 0)
{
yield return new LineBreak();
}
}
private IEnumerable<Inline> FormatCommandOutput(ConsoleCommand command)
{
bool firstLine = true;
if (command.command.Equals(ServerRcon.RCON_COMMAND_LISTPLAYERS, StringComparison.OrdinalIgnoreCase))
{
foreach (var output in command.lines)
{
var trimmed = output.TrimEnd();
if (!firstLine)
{
yield return new LineBreak();
}
firstLine = false;
if (trimmed == ServerRcon.NoResponseOutput)
{
yield return new RconOutput_NoResponse();
}
else if (trimmed.EndsWith("joined the game."))
{
yield return new RconOutput_PlayerJoined(trimmed);
}
else if (trimmed.EndsWith("left the game."))
{
yield return new RconOutput_PlayerLeft(trimmed);
}
else
{
yield return new RconOutput_CommandOutput(trimmed);
}
}
}
else
{
foreach (var output in command.lines)
{
var trimmed = output.TrimEnd();
if (!firstLine)
{
yield return new LineBreak();
}
firstLine = false;
if (trimmed == ServerRcon.NoResponseOutput)
{
yield return new RconOutput_NoResponse();
}
else
{
yield return new RconOutput_CommandOutput(trimmed);
}
}
}
}
private void RenderCommentsBlock(params string[] lines)
{
var p = new Paragraph();
bool firstLine = true;
foreach (var output in lines)
{
var trimmed = output.TrimEnd();
if (!firstLine)
{
p.Inlines.Add(new LineBreak());
}
firstLine = false;
p.Inlines.Add(new RconOutput_Comment(trimmed));
}
AddBlockContent(p);
}
private void RenderRCONCommandOutput(ConsoleCommand command)
{
//
// Format output
//
var p = new Paragraph();
if (!command.suppressCommand)
{
foreach (var element in FormatCommandInput(command))
{
p.Inlines.Add(element);
}
}
if (!command.suppressOutput)
{
foreach (var element in FormatCommandOutput(command))
{
p.Inlines.Add(element);
}
}
if (!(command.suppressCommand && command.suppressOutput))
{
if (p.Inlines.Count > 0)
{
AddBlockContent(p);
}
}
}
#endregion
#region Filtering
private void FilterPlayerList_Click(object sender, RoutedEventArgs e)
{
PlayersView?.Refresh();
}
public bool PlayerFilter(object obj)
{
var player = obj as PlayerInfo;
if (player == null)
return false;
var result = (this.PlayerFiltering.HasFlag(PlayerFilterType.Online) && player.IsOnline) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Offline) && !player.IsOnline) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Admin) && player.IsAdmin) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Whitelisted) && player.IsWhitelisted) ||
(this.PlayerFiltering.HasFlag(PlayerFilterType.Invalid) && !player.IsValid);
if (!result)
return false;
var filterString = PlayerFilterString.ToLower();
if (string.IsNullOrWhiteSpace(filterString))
return true;
result = player.PlatformNameFilterString != null && player.PlatformNameFilterString.Contains(filterString) ||
player.GuildNameFilterString != null && player.GuildNameFilterString.Contains(filterString) ||
player.CharacterNameFilterString != null && player.CharacterNameFilterString.Contains(filterString);
return result;
}
#endregion
}
}

View file

@ -0,0 +1,583 @@
<Window x:Class="ServerManagerTool.Windows.ServerMonitorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:enum="clr-namespace:ServerManagerTool.Enums"
xmlns:vm="clr-namespace:ServerManagerTool.Lib.ViewModel"
xmlns:tb="http://www.hardcodet.net/taskbar"
mc:Ignorable="d"
MinWidth="600" MinHeight="500" Width="900" Height="500" Left="50" Top="50" ResizeMode="CanResize"
Loaded="Window_Loaded" SizeChanged="Window_SizeChanged" StateChanged="Window_StateChanged"
Name="ServerMonitorUI" Icon="../Art/favicon.ico" Title="{DynamicResource ServerMonitor_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<cc:CommaDelimitedStringCountConverter x:Key="CommaDelimitedStringCountConverter"/>
<cc:InstalledVersionConverter x:Key="InstalledVersionConverter"/>
<vm:MapNameValueConverter x:Key="MapNameValueConverter"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}" MouseLeftButtonUp="OnMouseLeftButtonUp" MouseMove="OnMouseMove">
<DockPanel x:Name="dockPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Width="22" Height="22" Margin="5,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="CreateShortcut_Click" ToolTip="{DynamicResource ServerMonitor_CreateShortcutButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Shortcut.ico,Size=32}"/>
</Button>
<StackPanel Grid.Row="0" Grid.Column="1" 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.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowUpdateButton}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding ShowUpdateButton}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</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.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsStandAloneWindow}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsStandAloneWindow}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<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.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="White" />
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="Black"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="Background" Value="#26FF0000" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="Background" Value="#26FFA500" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="Background" Value="#26008000" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="Background" Value="#26FFA500" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="Background" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Background" Value="#26FF0000" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="Background" Value="#260000FF" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="Background" Value="#260000FF" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_ServerColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Profile.ProfileName}"/>
</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="150">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_MapColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Profile.ServerMap, Converter={StaticResource MapNameValueConverter}}"/>
</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="50">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_ModsColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Profile.ServerModIds, Converter={StaticResource CommaDelimitedStringCountConverter}}"/>
</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="80">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_VersionColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Profile.LastInstalledVersion, Converter={StaticResource InstalledVersionConverter}}"/>
</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="80">
<DataGridTemplateColumn.Header>
<TextBlock Text="{DynamicResource ServerMonitor_PlayersColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Label Content="{Binding Runtime.Players}" />
<Label Content="/" />
<Label Content="{Binding Runtime.MaxPlayers}"/>
</StackPanel>
</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_StatusColumnLabel}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Runtime.StatusString}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="StartStopServer_Click">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="ToolTip" Value="{DynamicResource ServerMonitor_StopServerTooltip}" />
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="ToolTip" Value="{DynamicResource ServerMonitor_StopServerTooltip}" />
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="ToolTip" Value="{DynamicResource ServerMonitor_StopServerTooltip}" />
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="ToolTip" Value="{DynamicResource ServerMonitor_StartServerTooltip}" />
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Start.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="UpdateServer_Click">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_UpgradeButtonTooltip}" />
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="ToolTip" Value="{DynamicResource ServerSettings_InstallButtonTooltip}" />
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OpenPlayerList_Click">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Profile.RconEnabled}" Value="False">
<Setter Property="ToolTip" Value="{DynamicResource ServerMonitor_PlayerListButtonTooltip}" />
</DataTrigger>
<DataTrigger Binding="{Binding Profile.RconEnabled}" Value="True">
<Setter Property="ToolTip" Value="{DynamicResource ServerMonitor_RCONButtonTooltip}" />
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Profile.RconEnabled}" Value="False">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Players.ico,Size=32}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Profile.RconEnabled}" Value="True">
<Setter Property="Source" Value="{com:Icon Path=/ConanServerManager;component/Art/Command.ico,Size=32}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="BackupServer_Click" ToolTip="{DynamicResource ServerSettings_SaveBackupButtonTooltip}">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Initializing}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopping}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Stopped}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Unknown}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Updating}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Runtime.Status}" Value="{x:Static enum:ServerStatus.Uninstalled}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Save.ico,Size=32}" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OpenServerFolder_Click" ToolTip="{DynamicResource ServerSettings_OpenServerFolderTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/FolderOpen.ico,Size=32}" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</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">
<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"/>
<TextBlock FontSize="14" VerticalAlignment="Center" Text="{Binding DraggedItem.Profile.ProfileName, ElementName=ServerMonitorUI}" Margin="8,0,0,0" />
</StackPanel>
</Border>
</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 Background="Black" Opacity="0.5"/>
<Border MinWidth="250" Background="Orange" BorderBrush="Black" BorderThickness="1" CornerRadius="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<Label x:Name="OverlayMessage" Margin="5" FontWeight="Bold" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Grid>
<tb:TaskbarIcon
Visibility="{Binding IsStandAloneWindow, Converter={StaticResource BooleanToVisibilityConverter}}"
ToolTipText="{Binding Title}"
IconSource="../Art/favicon.ico"
LeftClickCommand="{Binding ShowWindowCommand, ElementName=ServerMonitorUI}">
<tb:TaskbarIcon.Resources>
<clib:BindingProxy x:Key="proxy" Data="{Binding ElementName=ServerMonitorUI}"/>
</tb:TaskbarIcon.Resources>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="{DynamicResource Application_NotifyIcon_ShowServerMonitorWindow}" Command="{Binding Source={StaticResource proxy}, Path=Data.ShowWindowCommand}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</Grid>
</Window>

View file

@ -0,0 +1,800 @@
using IWshRuntimeLibrary;
using NLog;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Windows
{
/// <summary>
/// Interaction logic for ServerMonitorWindow.xaml
/// </summary>
public partial class ServerMonitorWindow : Window
{
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;
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 ServerMonitorWindow() : this(null)
{
}
public ServerMonitorWindow(ServerManager serverManager)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.ServerManager = serverManager;
this.IsStandAloneWindow = serverManager == null;
this.DataContext = this;
SetWindowTitle();
this.Height = Config.Default.ServerMonitorWindow_Height;
this.Width = Config.Default.ServerMonitorWindow_Width;
// hook into the language change event
GlobalizedApplication.Instance.GlobalizationManager.ResourceDictionaryChangedEvent += ResourceDictionaryChangedEvent;
Windows.Add(this);
}
public ServerManager ServerManager
{
get { return (ServerManager)GetValue(ServerManagerProperty); }
set { SetValue(ServerManagerProperty, value); }
}
public Version LatestServerManagerVersion
{
get { return (Version)GetValue(LatestServerManagerVersionProperty); }
set { SetValue(LatestServerManagerVersionProperty, value); }
}
public bool ShowUpdateButton
{
get { return (bool)GetValue(ShowUpdateButtonProperty); }
set { SetValue(ShowUpdateButtonProperty, value); }
}
public bool IsStandAloneWindow
{
get { return (bool)GetValue(IsStandAloneWindowProperty); }
set { SetValue(IsStandAloneWindowProperty, value); }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (ServerManager == null)
{
ServerManager = ServerManager.Instance;
TaskUtils.RunOnUIThreadAsync(() =>
{
// We need to load the set of existing servers, or create a blank one if we don't have any...
foreach (var profile in Directory.EnumerateFiles(Config.Default.ConfigPath, "*" + Config.Default.ProfileExtension))
{
try
{
ServerManager.Instance.AddFromPath(profile);
}
catch (Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("MainWindow_ProfileLoad_FailedLabel"), profile, ex.Message, ex.StackTrace), _globalizer.GetResourceString("MainWindow_ProfileLoad_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
ServerManager.Instance.SortServers();
ServerManager.Instance.CheckProfiles();
}).DoNotWait();
}
if (IsStandAloneWindow)
{
_versionChecker = new ActionQueue();
_versionChecker.PostAction(CheckForUpdates).DoNotWait();
}
}
private void Window_Closed(object sender, EventArgs e)
{
this.Activate();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (this.WindowState != WindowState.Minimized)
{
Config.Default.ServerMonitorWindow_Height = e.NewSize.Height;
Config.Default.ServerMonitorWindow_Width = e.NewSize.Width;
}
}
private void Window_StateChanged(object sender, EventArgs e)
{
if (IsStandAloneWindow && Config.Default.MainWindow_MinimizeToTray && this.WindowState == WindowState.Minimized)
{
this.Hide();
}
}
protected override void OnClosing(CancelEventArgs e)
{
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)
{
e.Cancel = true;
return;
}
}
Windows.Remove(this);
_versionChecker?.DisposeAsync().DoNotWait();
base.OnClosing(e);
}
private void ResourceDictionaryChangedEvent(object source, ResourceDictionaryChangedEventArgs e)
{
SetWindowTitle();
}
private async void BackupServer_Click(object sender, RoutedEventArgs e)
{
var server = ((Server)((Button)e.Source).DataContext);
if (server == null)
return;
try
{
var app = new ServerApp(true)
{
DeleteOldServerBackupFiles = !Config.Default.AutoBackup_EnableBackup,
SendEmails = false,
OutputLogs = false,
ServerProcess = ServerProcessType.Backup,
};
var profile = ServerProfileSnapshot.Create(server.Profile);
var exitCode = await Task.Run(() => app.PerformProfileBackup(profile));
if (exitCode != ServerApp.EXITCODE_NORMALEXIT && exitCode != ServerApp.EXITCODE_CANCELLED)
{
throw new ApplicationException($"An error occured during the backup process - ExitCode: {exitCode}");
}
MessageBox.Show(_globalizer.GetResourceString("ServerSettings_BackupServer_SuccessfulLabel"), _globalizer.GetResourceString("ServerSettings_BackupServer_Title"), MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_BackupServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void CreateShortcut_Click(object sender, RoutedEventArgs e)
{
var invalidChars = Path.GetInvalidFileNameChars().ToList();
var name = $"{_globalizer.GetResourceString("MainWindow_Title")} - {_globalizer.GetResourceString("ServerMonitor_Title")}";
invalidChars.ForEach(c => name = name.Replace(c.ToString(), ""));
var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
var shortcutLocation = Path.Combine(desktopPath, name + ".lnk");
var applicationFile = Assembly.GetExecutingAssembly().Location;
WshShell shell = new WshShell();
IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutLocation);
shortcut.Description = name;
shortcut.Arguments = "-sm";
shortcut.IconLocation = applicationFile;
shortcut.TargetPath = applicationFile;
shortcut.WorkingDirectory = Path.GetDirectoryName(applicationFile);
shortcut.Save();
}
private void OpenPlayerList_Click(object sender, RoutedEventArgs e)
{
var server = ((Server)((Button)e.Source).DataContext);
if (server == null)
return;
Window window = null;
if (server.Profile.RconEnabled)
{
window = RconWindow.GetRconForServer(server);
}
else
{
window = PlayerListWindow.GetWindowForServer(server);
}
window.Closed += Window_Closed;
window.Show();
if (window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
}
window.Focus();
}
private void OpenServerFolder_Click(object sender, RoutedEventArgs e)
{
var server = ((Server)((Button)e.Source).DataContext);
if (server == null)
return;
Process.Start("explorer.exe", server.Profile.InstallDirectory);
}
private void PatchNotes_Click(object sender, RoutedEventArgs e)
{
var url = string.Empty;
if (App.Instance.BetaVersion)
url = Config.Default.ServerManagerVersionBetaFeedUrl;
else
url = Config.Default.ServerManagerVersionFeedUrl;
if (!string.IsNullOrWhiteSpace(url))
{
var window = new VersionFeedWindow(url);
window.Closed += Window_Closed;
window.Owner = this;
window.ShowDialog();
}
else
{
if (App.Instance.BetaVersion)
url = Config.Default.LatestServerManagerBetaPatchNotesUrl;
else
url = Config.Default.LatestServerManagerPatchNotesUrl;
if (string.IsNullOrWhiteSpace(url))
return;
Process.Start(url);
}
}
private async void StartStopServer_Click(object sender, RoutedEventArgs e)
{
var server = ((Server)((Button)e.Source).DataContext);
if (server == null)
return;
await StartStopServerAsync(server);
}
private async void UpdateServer_Click(object sender, RoutedEventArgs e)
{
var server = ((Server)((Button)e.Source).DataContext);
if (server == null)
return;
switch (server.Runtime.Status)
{
case ServerStatus.Stopped:
case ServerStatus.Uninstalled:
break;
case ServerStatus.Running:
case ServerStatus.Initializing:
var result = MessageBox.Show(_globalizer.GetResourceString("ServerSettings_UpgradeServer_RunningLabel"), _globalizer.GetResourceString("ServerSettings_UpgradeServer_RunningTitle"), MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
return;
break;
case ServerStatus.Updating:
return;
}
try
{
server.Profile.Save(false, false, null);
await UpdateServerAsync(server, true, true, Config.Default.ServerUpdate_UpdateModsWhenUpdatingServer, false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_UpdateServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async void UpgradeApplication_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show(String.Format(_globalizer.GetResourceString("MainWindow_Upgrade_Label"), this.LatestServerManagerVersion), _globalizer.GetResourceString("MainWindow_Upgrade_Title"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
try
{
OverlayMessage.Content = _globalizer.GetResourceString("MainWindow_OverlayMessage_UpgradeLabel");
OverlayGrid.Visibility = Visibility.Visible;
await Task.Delay(500);
var process = Process.GetCurrentProcess();
if (process == null || process.HasExited)
throw new Exception("Application process could not be found or does not exist.");
var assemblyLocation = Assembly.GetEntryAssembly().Location;
var updaterFile = Path.Combine(Path.GetDirectoryName(assemblyLocation), Config.Default.UpdaterFile);
var newUpdaterFile = Path.Combine(Path.GetDirectoryName(assemblyLocation), $"New{Config.Default.UpdaterFile}");
// check if there is a new version of the updater file
if (System.IO.File.Exists(newUpdaterFile))
{
// file exists, rename the file, so that we use the new updater instead
try
{
System.IO.File.Copy(newUpdaterFile, updaterFile, true);
await Task.Delay(1000);
System.IO.File.Delete(newUpdaterFile);
}
catch (Exception ex)
{
// if error, then do nothing
Logger.Debug($"An error occurred trying to update the server manager updater. {ex.Message}");
}
}
if (!System.IO.File.Exists(updaterFile))
throw new FileNotFoundException("The updater application could not be found or does not exist.");
var arguments = new string[]
{
process.Id.ToString().AsQuoted(),
App.Instance.BetaVersion ? Config.Default.LatestServerManagerBetaDownloadUrl.AsQuoted() : Config.Default.LatestServerManagerDownloadUrl.AsQuoted(),
Config.Default.UpdaterPrefix.AsQuoted(),
};
ProcessStartInfo info = new ProcessStartInfo()
{
FileName = updaterFile.AsQuoted(),
Arguments = string.Join(" ", arguments),
UseShellExecute = true,
CreateNoWindow = true,
};
var updaterProcess = Process.Start(info);
if (updaterProcess == null)
throw new Exception("Could not restart application.");
}
catch (Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("MainWindow_Upgrade_FailedLabel"), ex.Message, ex.StackTrace), _globalizer.GetResourceString("MainWindow_Upgrade_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
OverlayGrid.Visibility = Visibility.Collapsed;
}
}
}
private async Task CheckForUpdates()
{
string url = App.Instance.BetaVersion ? Config.Default.LatestServerManagerBetaVersionUrl : Config.Default.LatestServerManagerVersionUrl;
var newVersion = await NetworkUtils.GetLatestServerManagerVersion(url);
TaskUtils.RunOnUIThreadAsync(() =>
{
try
{
var appVersion = new Version();
Version.TryParse(App.Instance.Version, out appVersion);
this.LatestServerManagerVersion = newVersion;
this.ShowUpdateButton = appVersion < newVersion;
Logger.Info($"{nameof(CheckForUpdates)} performed.");
}
catch (Exception)
{
// Ignore.
}
}).DoNotWait();
await Task.Delay(Config.Default.UpdateCheckTime * 60 * 1000);
_versionChecker?.PostAction(CheckForUpdates).DoNotWait();
}
public static void CloseAllWindows()
{
var windows = Windows.ToArray();
foreach (var window in windows)
{
if (window.IsLoaded)
window.Close();
}
Windows.Clear();
}
public static ServerMonitorWindow GetWindow(ServerManager serverManager)
{
if (Windows.Count > 0)
return Windows[0];
return new ServerMonitorWindow(serverManager);
}
private void SetWindowTitle()
{
if (!string.IsNullOrWhiteSpace(App.Instance.Title))
{
this.Title = App.Instance.Title;
}
else
{
this.Title = _globalizer.GetResourceString("MainWindow_Title");
}
this.Title += $" - {_globalizer.GetResourceString("ServerMonitor_Title")}";
if (IsStandAloneWindow)
{
this.Title += $" - {App.Instance.Version}";
}
}
private async Task StartStopServerAsync(Server server)
{
if (server == null)
return;
var serverRuntime = server.Runtime;
var serverProfile = server.Profile;
MessageBoxResult result = MessageBoxResult.None;
switch (serverRuntime.Status)
{
case ServerStatus.Initializing:
case ServerStatus.Running:
// check if the server is initialising.
if (serverRuntime.Status == ServerStatus.Initializing)
{
result = MessageBox.Show(_globalizer.GetResourceString("ServerSettings_StartServer_StartingLabel"), _globalizer.GetResourceString("ServerSettings_StartServer_StartingTitle"), MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
return;
try
{
PluginHelper.Instance.ProcessAlert(AlertType.Shutdown, serverProfile.ProfileName, Config.Default.Alert_ServerStopMessage);
await Task.Delay(2000);
await server.StopAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_StopServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
else
{
try
{
var shutdownWindow = ShutdownWindow.OpenShutdownWindow(server);
if (shutdownWindow == null)
{
MessageBox.Show(_globalizer.GetResourceString("ServerSettings_ShutdownServer_AlreadyOpenLabel"), _globalizer.GetResourceString("ServerSettings_ShutdownServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
shutdownWindow.Owner = Window.GetWindow(this);
shutdownWindow.Closed += Window_Closed;
shutdownWindow.Show();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_ShutdownServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
break;
case ServerStatus.Stopped:
Mutex mutex = null;
bool createdNew = false;
try
{
// try to establish a mutex for the profile.
mutex = new Mutex(true, ServerApp.GetMutexName(serverProfile.InstallDirectory), out createdNew);
// check if the mutex was established
if (createdNew)
{
serverProfile.Save(false, false, null);
if (Config.Default.ServerUpdate_OnServerStart)
{
if (!await UpdateServerAsync(server, false, true, Config.Default.ServerUpdate_UpdateModsWhenUpdatingServer, true))
{
if (MessageBox.Show(_globalizer.GetResourceString("ServerUpdate_WarningLabel"), _globalizer.GetResourceString("ServerUpdate_Title"), MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
return;
}
}
if (!serverProfile.Validate(false, out string validateMessage))
{
var outputMessage = _globalizer.GetResourceString("ProfileValidation_WarningLabel").Replace("{validateMessage}", validateMessage);
if (MessageBox.Show(outputMessage, _globalizer.GetResourceString("ProfileValidation_Title"), MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
return;
}
await server.StartAsync();
var startupMessage = Config.Default.Alert_ServerStartedMessage;
if (Config.Default.Alert_ServerStartedMessageIncludeIPandPort)
startupMessage += $" {Config.Default.MachinePublicIP}:{serverProfile.QueryPort}";
PluginHelper.Instance.ProcessAlert(AlertType.Startup, serverProfile.ProfileName, startupMessage);
await Task.Delay(2000);
}
else
{
// display an error message and exit
MessageBox.Show(_globalizer.GetResourceString("ServerSettings_StartServer_MutexFailedLabel"), _globalizer.GetResourceString("ServerSettings_StartServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_StartServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
if (mutex != null)
{
if (createdNew)
{
mutex.ReleaseMutex();
mutex.Dispose();
}
mutex = null;
}
}
break;
}
}
private async Task<bool> UpdateServerAsync(Server server, bool establishLock, bool updateServer, bool updateMods, bool closeProgressWindow)
{
if (server == null)
return false;
if (_upgradeCancellationSource != null)
{
// display an error message and exit
MessageBox.Show(_globalizer.GetResourceString("ServerMonitor_UpgradeServer_FailedLabel"), _globalizer.GetResourceString("ServerMonitor_UpgradeServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
return false;
}
ProgressWindow window = null;
Mutex mutex = null;
bool createdNew = !establishLock;
var serverProfile = server.Profile;
try
{
if (establishLock)
{
// try to establish a mutex for the profile.
mutex = new Mutex(true, ServerApp.GetMutexName(serverProfile.InstallDirectory), out createdNew);
}
// check if the mutex was established
if (createdNew)
{
_upgradeCancellationSource = new CancellationTokenSource();
window = new ProgressWindow(string.Format(_globalizer.GetResourceString("Progress_UpgradeServer_WindowTitle"), serverProfile.ProfileName))
{
Owner = Window.GetWindow(this)
};
window.Closed += Window_Closed;
window.Show();
await Task.Delay(1000);
var branch = new BranchSnapshot() { BranchName = serverProfile.BranchName, BranchPassword = serverProfile.BranchPassword };
return await server.UpgradeAsync(_upgradeCancellationSource.Token, updateServer, branch, true, updateMods, (p, m, n) => { TaskUtils.RunOnUIThreadAsync(() => { window?.AddMessage(m, n); }).DoNotWait(); });
}
else
{
// display an error message and exit
MessageBox.Show(_globalizer.GetResourceString("ServerSettings_UpgradeServer_MutexFailedLabel"), _globalizer.GetResourceString("ServerSettings_UpgradeServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
return false;
}
}
catch (Exception ex)
{
if (window != null)
{
window.AddMessage(ex.Message);
window.AddMessage(ex.StackTrace);
}
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_UpgradeServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
finally
{
_upgradeCancellationSource = null;
if (window != null)
{
window.CloseWindow();
if (closeProgressWindow)
window.Close();
}
if (mutex != null)
{
if (createdNew)
{
mutex.ReleaseMutex();
mutex.Dispose();
}
mutex = null;
}
}
}
public ICommand ShowWindowCommand
{
get
{
return new RelayCommand<object>(
execute: (parameter) =>
{
this.Show();
this.WindowState = WindowState.Normal;
this.Activate();
},
canExecute: (parameter) =>
{
return true;
}
);
}
}
#region Drag and Drop
public static readonly DependencyProperty DraggedItemProperty = DependencyProperty.Register(nameof(DraggedItem), typeof(Server), typeof(ServerMonitorWindow), new PropertyMetadata(null));
public Server DraggedItem
{
get { return (Server)GetValue(DraggedItemProperty); }
set { SetValue(DraggedItemProperty, value); }
}
public bool IsDragging { get; set; }
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ResetDragDrop();
// check fi the column is a template column (no drag-n-drop for those column types)
var cell = WindowUtils.TryFindFromPoint<DataGridCell>((UIElement)sender, e.GetPosition(ServersGrid));
if (cell != null) return;
// check if we have a valid row
var row = WindowUtils.TryFindFromPoint<DataGridRow>((UIElement)sender, e.GetPosition(ServersGrid));
if (row == null) return;
// set flag that indicates we're capturing mouse movements
IsDragging = true;
DraggedItem = (Server)row.Item;
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!IsDragging)
{
if (popup.IsOpen)
popup.IsOpen = false;
return;
}
//get the target item
var targetItem = (Server)ServersGrid.SelectedItem;
if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
{
//get target index
var targetIndex = ServerManager.Servers.IndexOf(targetItem);
//move source at the target's location
Move(DraggedItem, targetIndex);
//select the dropped item
ServersGrid.SelectedItem = DraggedItem;
}
//reset
ResetDragDrop();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (!IsDragging || e.LeftButton != MouseButtonState.Pressed)
{
if (popup.IsOpen)
popup.IsOpen = false;
return;
}
// display the popup if it hasn't been opened yet
if (!popup.IsOpen)
{
// switch to read-only mode
ServersGrid.IsReadOnly = true;
// make sure the popup is visible
popup.IsOpen = true;
}
var popupSize = new Size(popup.ActualWidth, popup.ActualHeight);
popup.PlacementRectangle = new Rect(e.GetPosition(this), popupSize);
// make sure the row under the grid is being selected
var position = e.GetPosition(ServersGrid);
var row = WindowUtils.TryFindFromPoint<DataGridRow>(ServersGrid, position);
if (row != null) ServersGrid.SelectedItem = row.Item;
}
public void Move(Server draggedItem, int newIndex)
{
if (draggedItem == null)
return;
var index = ServerManager.Servers.IndexOf(draggedItem);
if (index < 0)
return;
ServerManager.Servers.Move(index, newIndex);
UpdateSequence();
}
private void ResetDragDrop()
{
IsDragging = false;
popup.IsOpen = false;
ServersGrid.IsReadOnly = false;
}
public void UpdateSequence()
{
foreach (var server in ServerManager.Servers)
{
server.Profile.Sequence = ServerManager.Servers.IndexOf(server) + 1;
}
}
#endregion
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
<Window x:Class="ServerManagerTool.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sm="clr-namespace:ServerManagerTool"
mc:Ignorable="d"
MinWidth="800" MinHeight="600" Width="800" Height="700" ResizeMode="CanResizeWithGrip" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Closing="SettingsWindow_Closing"
Icon="../Art/favicon.ico" Title="{DynamicResource Settings_Title}" >
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid>
<sm:GlobalSettingsControl x:Name="globalSettingsControl" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch" Background="{StaticResource BeigeGradient}"/>
</Grid>
</Window>

View file

@ -0,0 +1,113 @@
using ServerManagerTool.Common;
using ServerManagerTool.Common.Utils;
using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow : Window
{
public static readonly DependencyProperty AppInstanceProperty = DependencyProperty.Register(nameof(AppInstance), typeof(App), typeof(SettingsWindow), new PropertyMetadata(null));
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public SettingsWindow()
{
this.AppInstance = App.Instance;
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
}
public App AppInstance
{
get { return GetValue(AppInstanceProperty) as App; }
set { SetValue(AppInstanceProperty, value); }
}
private void SettingsWindow_Closing(object sender, CancelEventArgs e)
{
if (!globalSettingsControl.IsEnabled)
e.Cancel = true;
}
protected override void OnClosed(EventArgs e)
{
if (SecurityUtils.IsAdministrator())
{
// check if the Auto Update has been enabled.
if (Config.Default.AutoUpdate_EnableUpdate)
{
// check if an update period has been set.
if (Config.Default.AutoUpdate_UpdatePeriod <= 0)
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_CacheUpdate_DisabledLabel"), _globalizer.GetResourceString("GlobalSettings_CacheUpdate_DisabledTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// check if the cache directory has been set and it exists.
if (string.IsNullOrWhiteSpace(Config.Default.AutoUpdate_CacheDir) || !Directory.Exists(Config.Default.AutoUpdate_CacheDir))
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_CacheDirectory_ErrorLabel"), _globalizer.GetResourceString("GlobalSettings_CacheDirectory_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
var taskKey = TaskSchedulerUtils.ComputeKey(Config.Default.DataPath);
var command = Assembly.GetEntryAssembly().Location;
if (!TaskSchedulerUtils.ScheduleAutoUpdate(taskKey, null, command, Config.Default.AutoUpdate_EnableUpdate ? Config.Default.AutoUpdate_UpdatePeriod : 0))
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_CacheTaskUpdate_ErrorLabel"), _globalizer.GetResourceString("GlobalSettings_CacheTaskUpdate_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
if (Config.Default.AutoUpdate_EnableUpdate && Config.Default.AutoUpdate_UpdatePeriod > 0)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("GlobalSettings_CacheUpdate_EnabledLabel"), Config.Default.AutoUpdate_UpdatePeriod), _globalizer.GetResourceString("GlobalSettings_CacheUpdate_EnabledTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_CacheUpdate_DisabledLabel"), _globalizer.GetResourceString("GlobalSettings_CacheUpdate_DisabledTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
}
if (!TaskSchedulerUtils.ScheduleAutoBackup(taskKey, null, command, Config.Default.AutoBackup_EnableBackup ? Config.Default.AutoBackup_BackupPeriod : 0))
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_BackupTaskUpdate_ErrorLabel"), _globalizer.GetResourceString("GlobalSettings_BackupTaskUpdate_ErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
if (Config.Default.AutoBackup_EnableBackup && Config.Default.AutoBackup_BackupPeriod > 0)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("GlobalSettings_BackupTaskUpdate_EnabledLabel"), Config.Default.AutoBackup_BackupPeriod), _globalizer.GetResourceString("GlobalSettings_BackupTaskUpdate_EnabledTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_BackupTaskUpdate_DisabledLabel"), _globalizer.GetResourceString("GlobalSettings_BackupTaskUpdate_DisabledTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
if (Config.Default.SteamCmdRedirectOutput && !Config.Default.SteamCmd_UseAnonymousCredentials)
{
MessageBox.Show(_globalizer.GetResourceString("GlobalSettings_SteamCMDAuthentication_DisabledLabel"), _globalizer.GetResourceString("GlobalSettings_SteamCMDAuthentication_DisabledTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
Config.Default.SteamCmd_UseAnonymousCredentials = true;
}
Config.Default.Save();
CommonConfig.Default.Save();
Config.Default.Reload();
CommonConfig.Default.Reload();
base.OnClosed(e);
}
}
}

View file

@ -0,0 +1,254 @@
<Window x:Class="ServerManagerTool.ShutdownWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cctl="clr-namespace:ServerManagerTool.Common.Controls;assembly=ServerManager.Common"
xmlns:cc="clr-namespace:ServerManagerTool.Common.Converters;assembly=ServerManager.Common"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:enum="clr-namespace:ServerManagerTool.Enums"
Width="600" ResizeMode="CanMinimize" SizeToContent="Height" WindowStyle="SingleBorderWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="True"
Closing="Window_Closing" Closed="Window_Closed"
Icon="../Art/favicon.ico" Title="{DynamicResource ShutdownWindow_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<cc:InvertBooleanConverter x:Key="InvertBooleanConverter"/>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="BeigeLabel" Color="#FFE6DFD8"/>
<SolidColorBrush x:Key="BeigeBorder" Color="#FFD8CCBC"/>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="BorderBrush" Value="{StaticResource BeigeBorder}"/>
</Style>
<ContentControl x:Key="ShutdownButtonContent">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="5,0,5,0" Source="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}" VerticalAlignment="Center"/>
<Label Content="{DynamicResource ShutdownWindow_ShutdownButtonLabel}" VerticalAlignment="Center" Padding="5,0"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="RestartButtonContent">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="5,0,5,0" Source="{com:Icon Path=/ConanServerManager;component/Art/Start.ico,Size=32}" VerticalAlignment="Center"/>
<Label Content="{DynamicResource ShutdownWindow_RestartButtonLabel}" VerticalAlignment="Center" Padding="5,0"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="CancelShutdownButtonContent">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="5,0,5,0" Source="{com:Icon Path=/ConanServerManager;component/Art/Cancel.ico,Size=32}" VerticalAlignment="Center"/>
<Label Content="{DynamicResource ShutdownWindow_CancelShutdownButtonLabel}" VerticalAlignment="Center" Padding="5,0"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="StopButtonContent">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="5,0,5,0" Source="{com:Icon Path=/ConanServerManager;component/Art/Stop.ico,Size=32}" VerticalAlignment="Center"/>
<Label Content="{DynamicResource ShutdownWindow_StopButtonLabel}" VerticalAlignment="Center" Padding="5,0"/>
</StackPanel>
</ContentControl>
<ContentControl x:Key="CancelButtonContent">
<StackPanel Orientation="Horizontal">
<Label Content="{DynamicResource ShutdownWindow_CloseButtonLabel}" VerticalAlignment="Center" Padding="5,0"/>
</StackPanel>
</ContentControl>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" DataContext="{Binding Server.Runtime}" Margin="2,0,2,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="0,2,0,2" Background="{DynamicResource BeigeLabel}" ToolTip="{DynamicResource ServerSettings_StatusTooltip}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Label FontSize="15" Content="{DynamicResource ServerSettings_StatusLabel}"/>
<Label FontSize="15" Content="{Binding StatusString}" MinWidth="100"/>
</StackPanel>
</Border>
<Border Grid.Column="1" Margin="2,2,0,2" Background="{DynamicResource BeigeLabel}" ToolTip="{DynamicResource ServerSettings_PlayersTooltip}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Label FontSize="15" Content="{DynamicResource ServerSettings_PlayersLabel}"/>
<Label FontSize="15" Content="{Binding Players}"/>
<Label FontSize="15" Content="/" Width="20"/>
<Label FontSize="15" Content="{Binding MaxPlayers}"/>
</StackPanel>
</Border>
</Grid>
<GroupBox Grid.Row="1" Style="{StaticResource GroupBoxStyle}" Margin="2,0,2,2">
<GroupBox.Header>
<Label Content="{DynamicResource ShutdownWindow_ShutdownRestartOptionsLabel}"/>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" Margin="5" Content="{DynamicResource ShutdownWindow_CheckForOnlinePlayersLabel}" IsChecked="{Binding CheckForOnlinePlayers}" IsEnabled="{Binding ShutdownStarted, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ShutdownWindow_CheckForOnlinePlayersTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="0" Grid.Column="1" Margin="5" Content="{DynamicResource ShutdownWindow_SendShutdownMessagesLabel}" IsChecked="{Binding SendShutdownMessages}" IsEnabled="{Binding ShutdownStarted, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ShutdownWindow_SendShutdownMessagesTooltip}" HorizontalAlignment="Left"/>
<cctl:AnnotatedSlider Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="1" Label="{DynamicResource ShutdownWindow_ShutdownIntervalLabel}" Value="{Binding ShutdownInterval}" Minimum="0" Maximum="60" SmallChange="1" LargeChange="5" TickFrequency="1" LabelRelativeWidth="Auto" SliderRelativeWidth="15*" SuffixRelativeWidth="Auto" Suffix="{DynamicResource SliderUnits_Minutes}" IsEnabled="{Binding ShutdownStarted, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ShutdownWindow_ShutdownIntervalTooltip}"/>
<CheckBox Grid.Row="2" Grid.Column="0" Margin="5" Content="{DynamicResource ShutdownWindow_BackupWorldFileLabel}" IsChecked="{Binding BackupWorldFile}" IsEnabled="{Binding ShutdownStarted, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ShutdownWindow_BackupWorldFileTooltip}" HorizontalAlignment="Left">
<CheckBox.Style>
<Style BasedOn="{StaticResource {x:Type CheckBox}}" TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Server.Profile.SOTF_Enabled}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Server.Profile.SOTF_Enabled}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<CheckBox Grid.Row="2" Grid.Column="1" Margin="5" Content="{DynamicResource ShutdownWindow_UpdateServerLabel}" IsChecked="{Binding UpdateServer}" IsEnabled="{Binding ShutdownStarted, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ShutdownWindow_UpdateServerTooltip}" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="3" Grid.Column="0" Margin="5" Content="{DynamicResource ShutdownWindow_RestartServerLabel}" IsChecked="{Binding RestartServer}" IsEnabled="{Binding ShutdownStarted, Converter={StaticResource InvertBooleanConverter}}" ToolTip="{DynamicResource ShutdownWindow_RestartServerTooltip}" HorizontalAlignment="Left"/>
<Grid Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{DynamicResource ShutdownWindow_ShutdownReasonLabel}" ToolTip="{DynamicResource ShutdownWindow_ShutdownReasonTooltip}"/>
<TextBox Grid.Column="1" Text="{Binding ShutdownReason}" ToolTip="{DynamicResource ShutdownWindow_ShutdownReasonTooltip}"/>
</Grid>
<TextBox Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Name="MessageOutput" Margin="1" Height="200" HorizontalAlignment="Stretch" IsReadOnly="True" IsReadOnlyCaretVisible="True" TextWrapping="NoWrap" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TextBox.Style>
<Style BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding Server.Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ShutdownType}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding ShutdownType}" Value="2">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</GroupBox>
<Grid Grid.Row="2" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="75" Width="Auto"/>
<ColumnDefinition MinWidth="75" Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="75" Width="Auto"/>
<ColumnDefinition MinWidth="75" Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Margin="0" Padding="5" VerticalAlignment="Center" Click="Shutdown_Click">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Content" Value="{StaticResource ShutdownButtonContent}" />
<Setter Property="ToolTip" Value="{DynamicResource ShutdownWindow_ShutdownButtonTooltip}" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Server.Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}" />
<Condition Binding="{Binding ShutdownStarted}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
<DataTrigger Binding="{Binding RestartServer}" Value="True">
<Setter Property="Content" Value="{StaticResource RestartButtonContent}"/>
<Setter Property="ToolTip" Value="{DynamicResource ShutdownWindow_RestartButtonTooltip}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Grid.Row="0" Grid.Column="1" Margin="5,0,0,0" Padding="5" VerticalAlignment="Center" Name="CancelShutdownButton" Click="CancelShutdown_Click">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="Content" Value="{StaticResource CancelShutdownButtonContent}" />
<Setter Property="ToolTip" Value="{DynamicResource ShutdownWindow_CancelShutdownButtonTooltip}" />
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Server.Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}" />
<Condition Binding="{Binding ShutdownStarted}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Grid.Row="0" Grid.Column="3" Margin="5,0,0,0" Padding="5" VerticalAlignment="Center" Click="Stop_Click">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Content" Value="{StaticResource StopButtonContent}" />
<Setter Property="ToolTip" Value="{DynamicResource ShutdownWindow_StopButtonTooltip}" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Server.Runtime.Status}" Value="{x:Static enum:ServerStatus.Running}" />
<Condition Binding="{Binding ShutdownStarted}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Grid.Row="0" Grid.Column="4" Margin="5,0,0,0" Padding="5" VerticalAlignment="Center" Click="Cancel_Click">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="True" />
<Setter Property="Content" Value="{StaticResource CancelButtonContent}" />
<Setter Property="ToolTip" Value="{DynamicResource ShutdownWindow_CloseButtonTooltip}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ShutdownStarted}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,276 @@
using ServerManagerTool.Common;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using WPFSharp.Globalizer;
using static ServerManagerTool.Lib.ServerApp;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for ShutdownWindow.xaml
/// </summary>
public partial class ShutdownWindow : Window
{
private static readonly List<Server> Instances = new List<Server>();
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private CancellationTokenSource _shutdownCancellationSource = null;
public static readonly DependencyProperty CheckForOnlinePlayersProperty = DependencyProperty.Register(nameof(CheckForOnlinePlayers), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(Config.Default.ServerShutdown_CheckForOnlinePlayers));
public static readonly DependencyProperty SendShutdownMessagesProperty = DependencyProperty.Register(nameof(SendShutdownMessages), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(Config.Default.ServerShutdown_SendShutdownMessages));
public static readonly DependencyProperty BackupWorldFileProperty = DependencyProperty.Register(nameof(BackupWorldFile), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(Config.Default.BackupWorldFile));
public static readonly DependencyProperty RestartServerProperty = DependencyProperty.Register(nameof(RestartServer), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(false));
public static readonly DependencyProperty ShowMessageOutputProperty = DependencyProperty.Register(nameof(ShowMessageOutput), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(false));
public static readonly DependencyProperty ShutdownIntervalProperty = DependencyProperty.Register(nameof(ShutdownInterval), typeof(int), typeof(ShutdownWindow), new PropertyMetadata(Config.Default.ServerShutdown_GracePeriod));
public static readonly DependencyProperty ShutdownStartedProperty = DependencyProperty.Register(nameof(ShutdownStarted), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(false));
public static readonly DependencyProperty ShutdownTypeProperty = DependencyProperty.Register(nameof(ShutdownType), typeof(int), typeof(ShutdownWindow), new PropertyMetadata(0));
public static readonly DependencyProperty ServerProperty = DependencyProperty.Register(nameof(Server), typeof(Server), typeof(ShutdownWindow), new PropertyMetadata(null));
public static readonly DependencyProperty ShutdownReasonProperty = DependencyProperty.Register(nameof(ShutdownReason), typeof(string), typeof(ShutdownWindow), new PropertyMetadata(null));
public static readonly DependencyProperty UpdateServerProperty = DependencyProperty.Register(nameof(UpdateServer), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(false));
public static readonly DependencyProperty CloseShutdownWindowWhenFinishedProperty = DependencyProperty.Register(nameof(CloseShutdownWindowWhenFinished), typeof(bool), typeof(ShutdownWindow), new PropertyMetadata(false));
protected ShutdownWindow(Server server)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
Server = server;
BackupWorldFile = Config.Default.BackupWorldFile;
this.Title = string.Format(_globalizer.GetResourceString("ShutdownWindow_ProfileTitle"), server?.Profile?.ProfileName);
this.CloseShutdownWindowWhenFinished = Config.Default.CloseShutdownWindowWhenFinished;
this.DataContext = this;
}
public bool CheckForOnlinePlayers
{
get { return (bool)GetValue(CheckForOnlinePlayersProperty); }
set { SetValue(CheckForOnlinePlayersProperty, value); }
}
public bool SendShutdownMessages
{
get { return (bool)GetValue(SendShutdownMessagesProperty); }
set { SetValue(SendShutdownMessagesProperty, value); }
}
public bool BackupWorldFile
{
get { return (bool)GetValue(BackupWorldFileProperty); }
set { SetValue(BackupWorldFileProperty, value); }
}
public bool RestartServer
{
get { return (bool)GetValue(RestartServerProperty); }
set { SetValue(RestartServerProperty, value); }
}
public bool ShowMessageOutput
{
get { return (bool)GetValue(ShowMessageOutputProperty); }
set { SetValue(ShowMessageOutputProperty, value); }
}
public int ShutdownInterval
{
get { return (int)GetValue(ShutdownIntervalProperty); }
set { SetValue(ShutdownIntervalProperty, value); }
}
public bool ShutdownStarted
{
get { return (bool)GetValue(ShutdownStartedProperty); }
set { SetValue(ShutdownStartedProperty, value); }
}
public int ShutdownType
{
get { return (int)GetValue(ShutdownTypeProperty); }
set
{
SetValue(ShutdownTypeProperty, value);
ShowMessageOutput = value == 1;
ShutdownStarted = value > 0;
}
}
public Server Server
{
get { return (Server)GetValue(ServerProperty); }
set { SetValue(ServerProperty, value); }
}
public string ShutdownReason
{
get { return (string)GetValue(ShutdownReasonProperty); }
set { SetValue(ShutdownReasonProperty, value); }
}
public bool UpdateServer
{
get { return (bool)GetValue(UpdateServerProperty); }
set { SetValue(UpdateServerProperty, value); }
}
public bool CloseShutdownWindowWhenFinished
{
get { return (bool)GetValue(CloseShutdownWindowWhenFinishedProperty); }
set { SetValue(CloseShutdownWindowWhenFinishedProperty, value); }
}
private void Window_Closing(object sender, CancelEventArgs e)
{
if (ShutdownStarted)
e.Cancel = true;
}
private void Window_Closed(object sender, EventArgs e)
{
Instances.Remove(Server);
Server = null;
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
if (ShutdownStarted)
return;
// close the form.
this.Close();
}
private void CancelShutdown_Click(object sender, RoutedEventArgs e)
{
if (_shutdownCancellationSource == null || _shutdownCancellationSource.IsCancellationRequested)
return;
_shutdownCancellationSource.Cancel();
}
private async void Shutdown_Click(object sender, RoutedEventArgs e)
{
await StartShutdownAsync();
}
private async void Stop_Click(object sender, RoutedEventArgs e)
{
if (ShutdownStarted)
return;
try
{
ShutdownType = 2;
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
PluginHelper.Instance.ProcessAlert(AlertType.Shutdown, Server.Profile.ProfileName, Config.Default.Alert_ServerStopMessage);
await Task.Delay(2000);
await this.Server.StopAsync();
ShutdownType = 0;
this.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_StopServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
ShutdownType = 0;
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = null);
}
}
public void AddMessage(string message, bool includeNewLine = true)
{
MessageOutput.AppendText(message);
if (includeNewLine)
MessageOutput.AppendText(Environment.NewLine);
MessageOutput.ScrollToEnd();
Debug.WriteLine(message);
}
public static bool HasInstance(Server server)
{
return Instances.Contains(server);
}
public static ShutdownWindow OpenShutdownWindow(Server server)
{
if (HasInstance(server))
return null;
Instances.Add(server);
return new ShutdownWindow(server);
}
public async Task StartShutdownAsync()
{
if (ShutdownStarted)
return;
try
{
MessageOutput.Clear();
ShutdownType = 1;
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
Application.Current.Dispatcher.Invoke(() => this.CancelShutdownButton.Cursor = System.Windows.Input.Cursors.Arrow);
var app = new ServerApp(true)
{
CheckForOnlinePlayers = this.CheckForOnlinePlayers,
SendMessages = this.SendShutdownMessages,
BackupWorldFile = this.BackupWorldFile,
ShutdownInterval = this.ShutdownInterval,
ShutdownReason = this.ShutdownReason,
OutputLogs = false,
SendAlerts = true,
ServerProcess = RestartServer ? ServerProcessType.Restart : ServerProcessType.Shutdown,
ProgressCallback = (p, m, n) => { TaskUtils.RunOnUIThreadAsync(() => { this.AddMessage(m, n); }).DoNotWait(); },
};
// if restarting the serverm, then check and update the public IP address
if (RestartServer && Config.Default.ManagePublicIPAutomatically)
{
await App.DiscoverMachinePublicIPAsync(false);
}
var profile = ServerProfileSnapshot.Create(Server.Profile);
var restartServer = RestartServer;
var updateServer = UpdateServer;
_shutdownCancellationSource = new CancellationTokenSource();
var exitCode = await Task.Run(() => app.PerformProfileShutdown(profile, restartServer, updateServer, 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}");
if (restartServer)
{
profile.Update(Server.Profile);
Server.Profile.SaveProfile();
}
ShutdownType = 0;
// if restarting or updating the server after the shutdown, delay the form closing
if (restartServer || updateServer)
await Task.Delay(5000);
if (this.CloseShutdownWindowWhenFinished)
this.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("ServerSettings_ShutdownServer_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
ShutdownType = 0;
}
finally
{
_shutdownCancellationSource = null;
Application.Current.Dispatcher.Invoke(() => this.Cursor = null);
Application.Current.Dispatcher.Invoke(() => this.CancelShutdownButton.Cursor = null);
}
}
}
}

View file

@ -0,0 +1,65 @@
<Window x:Class="ServerManagerTool.VersionFeedWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
xmlns:clib="clr-namespace:ServerManagerTool.Common.Lib;assembly=ServerManager.Common"
xmlns:cmod="clr-namespace:ServerManagerTool.Common.Model;assembly=ServerManager.Common"
MinWidth="400" MinHeight="400" Width="640" Height="480" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" ResizeMode="CanResizeWithGrip"
Loaded="Window_Loaded"
Icon="../Art/favicon.ico" Title="{DynamicResource VersionFeedWindow_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="Version" TargetType="Label">
<Setter Property="Content" Value="{Binding Title}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsCurrent}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="#0066CC"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource GradientBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Margin="5,5,0,0" Content="{DynamicResource VersionFeedWindow_VersionFilterLabel}"/>
<ComboBox Grid.Row="0" Grid.Column="1" Margin="5,5,5,0" ItemsSource="{Binding FeedEntries}" SelectedValue="{Binding SelectedFeedEntry}" ToolTip="{DynamicResource VersionFeedWindow_VersionFilterTooltip}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}" >
<Setter Property="Height" Value="20" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type cmod:VersionFeedEntry}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<Label Padding="0,-1,0,-1" VerticalAlignment="Center" Style="{DynamicResource Version}"/>
<TextBlock Text="{Binding Updated, StringFormat= - {0:G}}" Margin="5,0,0,0" Padding="0,-1,0,-1" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Row="0" Grid.Column="2" Width="22" Height="22" Margin="5,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="PatchNotes_Click" ToolTip="{DynamicResource ServerSettings_PatchNotesTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Website.ico,Size=32}"/>
</Button>
<WebBrowser Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="5" clib:BrowserBehavior.Html="{Binding SelectedFeedEntry.Content}"/>
</Grid>
</Window>

View file

@ -0,0 +1,106 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Markup;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for VersionFeedWindow.xaml
/// </summary>
public partial class VersionFeedWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty AppInstanceProperty = DependencyProperty.Register(nameof(AppInstance), typeof(App), typeof(VersionFeedWindow), new PropertyMetadata(null));
public static readonly DependencyProperty FeedEntriesProperty = DependencyProperty.Register(nameof(FeedEntries), typeof(ObservableCollection<VersionFeedEntry>), typeof(VersionFeedWindow), new PropertyMetadata(new ObservableCollection<VersionFeedEntry>()));
public static readonly DependencyProperty SelectedFeedEntryProperty = DependencyProperty.Register(nameof(SelectedFeedEntry), typeof(VersionFeedEntry), typeof(VersionFeedWindow), new PropertyMetadata(null));
private string feedUri = string.Empty;
public VersionFeedWindow(string feedUri)
{
this.AppInstance = App.Instance;
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
this.feedUri = feedUri;
this.DataContext = this;
}
public App AppInstance
{
get { return GetValue(AppInstanceProperty) as App; }
set { SetValue(AppInstanceProperty, value); }
}
public ObservableCollection<VersionFeedEntry> FeedEntries
{
get { return (ObservableCollection<VersionFeedEntry>)GetValue(FeedEntriesProperty); }
set { SetValue(FeedEntriesProperty, value); }
}
public VersionFeedEntry SelectedFeedEntry
{
get { return (VersionFeedEntry)GetValue(SelectedFeedEntryProperty); }
set { SetValue(SelectedFeedEntryProperty, value); }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
LoadFeed();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("VersionFeedWindow_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void PatchNotes_Click(object sender, RoutedEventArgs e)
{
var url = string.Empty;
if (AppInstance.BetaVersion)
url = Config.Default.LatestServerManagerBetaPatchNotesUrl;
else
url = Config.Default.LatestServerManagerPatchNotesUrl;
if (string.IsNullOrWhiteSpace(url))
return;
Process.Start(url);
}
private void LoadFeed()
{
FeedEntries.Clear();
if (string.IsNullOrWhiteSpace(this.feedUri))
return;
var versionFeed = VersionFeedUtils.LoadVersionFeed(this.feedUri, AppInstance.Version);
if (versionFeed == null)
return;
foreach (var entry in versionFeed.Entries)
{
if (entry == null)
continue;
FeedEntries.Add(entry);
}
SelectedFeedEntry = FeedEntries.OrderByDescending(e => e.Updated).FirstOrDefault();
}
}
}

View file

@ -0,0 +1,147 @@
<Window x:Class="ServerManagerTool.WorkshopFilesWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
MinWidth="700" MinHeight="480" Width="800" Height="480" ResizeMode="CanResize" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Loaded="Window_Loaded"
Icon="../Art/favicon.ico" Title="{DynamicResource WorkshopFiles_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Width="22" Height="22" Margin="5,5,5,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="Reload_Click" ToolTip="{DynamicResource WorkshopFiles_ReloadTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Reload.ico,Size=32}"/>
</Button>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
<TextBlock Margin="10,5,5,0" Text="{DynamicResource WorkshopFiles_TotalCountLabel}" VerticalAlignment="Center" />
<TextBlock Margin="5,5,5,0" Text="{Binding WorkshopFiles.Count}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
<TextBlock Margin="30,5,5,0" Text="{DynamicResource WorkshopFiles_LastRefreshedLabel}" VerticalAlignment="Center" />
<TextBlock Margin="5,5,5,0" Text="{Binding WorkshopFiles.CachedTimeFormatted}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,5,0,0" Orientation="Horizontal" Height="30" DataContext="{Binding}">
<TextBlock Margin="5,0,5,0" Text="{DynamicResource General_FilterLabel}" VerticalAlignment="Center" />
<TextBox Margin="5,0,5,0" Text="{Binding WorkshopFilterString, Mode=TwoWay}" Width="200" Padding="2" VerticalAlignment="Center"/>
<Button Margin="5,0,5,0" Width="22" Height="22" HorizontalAlignment="Left" VerticalAlignment="Center" Click="FilterWorkshopFiles_Click" ToolTip="{DynamicResource General_FilterButtonTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Filter.ico,Size=32}"/>
</Button>
<CheckBox Margin="5,0,5,0" IsChecked="{Binding WorkshopFilterExisting, Mode=TwoWay}" Content="{DynamicResource WorkshopFiles_FilterExistingLabel}" VerticalAlignment="Center" VerticalContentAlignment="Center"/>
</StackPanel>
<DataGrid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding WorkshopFilesView}" Margin="5" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserSortColumns="True" RowHeaderWidth="0" SelectionMode="Single">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
</Style.Resources>
</Style>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Add_Click" ToolTip="{DynamicResource WorkshopFiles_AddTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Add.ico,Size=32}"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="Auto" MinWidth="100" CanUserSort="True" SortMemberPath="WorkshopId" Header="{DynamicResource WorkshopFiles_ModIdColumnLabel}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink NavigateUri="{Binding WorkshopUrl}" RequestNavigate="RequestNavigate_Click">
<TextBlock Text="{Binding WorkshopId}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="1*" Binding="{Binding Title, Mode=OneWay}" Header="{DynamicResource WorkshopFiles_TitleColumnLabel}"/>
<DataGridTextColumn Width="130" Binding="{Binding CreatedDate, Mode=OneWay}" Header="{DynamicResource WorkshopFiles_CreatedDateColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="130" Binding="{Binding UpdatedDate, Mode=OneWay}" Header="{DynamicResource WorkshopFiles_UpdatedDateColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="80" Binding="{Binding Subscriptions, Mode=OneWay}" Header="{DynamicResource WorkshopFiles_SubscriptionsColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="80" Binding="{Binding FileSize, Mode=OneWay}" Header="{DynamicResource WorkshopFiles_FileSizeColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

View file

@ -0,0 +1,237 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Lib;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Navigation;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for WorkshopFilesWindow.xaml
/// </summary>
public partial class WorkshopFilesWindow : Window
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private readonly ServerProfile _profile = null;
private ModDetailList _modDetails = null;
private readonly ModDetailsWindow _window = null;
public static readonly DependencyProperty WorkshopFilesProperty = DependencyProperty.Register(nameof(WorkshopFiles), typeof(WorkshopFileList), typeof(WorkshopFilesWindow), new PropertyMetadata(null));
public static readonly DependencyProperty WorkshopFilesViewProperty = DependencyProperty.Register(nameof(WorkshopFilesView), typeof(ICollectionView), typeof(WorkshopFilesWindow), new PropertyMetadata(null));
public static readonly DependencyProperty WorkshopFilterStringProperty = DependencyProperty.Register(nameof(WorkshopFilterString), typeof(string), typeof(WorkshopFilesWindow), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty WorkshopFilterExistingProperty = DependencyProperty.Register(nameof(WorkshopFilterExisting), typeof(bool), typeof(WorkshopFilesWindow), new PropertyMetadata(false));
public WorkshopFilesWindow(ModDetailList modDetails, ServerProfile profile)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
_profile = profile;
this.Title = string.Format(_globalizer.GetResourceString("WorkshopFiles_ProfileTitle"), _profile?.ProfileName);
UpdateModDetailsList(modDetails);
this.DataContext = this;
}
public WorkshopFilesWindow(ModDetailsWindow window, ServerProfile profile)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
_window = window;
_profile = profile;
this.Title = string.Format(_globalizer.GetResourceString("WorkshopFiles_ProfileTitle"), _profile?.ProfileName);
UpdateModDetailsList(window?.ModDetails);
this.DataContext = this;
}
public WorkshopFileList WorkshopFiles
{
get { return GetValue(WorkshopFilesProperty) as WorkshopFileList; }
set
{
SetValue(WorkshopFilesProperty, value);
WorkshopFilesView = CollectionViewSource.GetDefaultView(WorkshopFiles);
WorkshopFilesView.Filter = new Predicate<object>(Filter);
}
}
public ICollectionView WorkshopFilesView
{
get { return GetValue(WorkshopFilesViewProperty) as ICollectionView; }
set { SetValue(WorkshopFilesViewProperty, value); }
}
public string WorkshopFilterString
{
get { return (string)GetValue(WorkshopFilterStringProperty); }
set { SetValue(WorkshopFilterStringProperty, value); }
}
public bool WorkshopFilterExisting
{
get { return (bool)GetValue(WorkshopFilterExistingProperty); }
set { SetValue(WorkshopFilterExistingProperty, value); }
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
await LoadWorkshopItems(true);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("WorkshopFiles_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ModDetails_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
WorkshopFilesView?.Refresh();
}
private void Add_Click(object sender, RoutedEventArgs e)
{
var item = ((WorkshopFileItem)((Button)e.Source).DataContext);
var mod = ModDetail.GetModDetail(item);
var selectedIndex = _window?.SelectedRowIndex() ?? -1;
if (selectedIndex >= 0)
_modDetails.Insert(selectedIndex, mod);
else
_modDetails.Add(mod);
}
private async void Reload_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await LoadWorkshopItems(false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("WorkshopFiles_Refresh_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private void RequestNavigate_Click(object sender, RequestNavigateEventArgs e)
{
var item = ((WorkshopFileItem)((Hyperlink)e.Source).DataContext);
Process.Start(new ProcessStartInfo(item.WorkshopUrl));
e.Handled = true;
}
private async Task LoadWorkshopItems(bool loadFromCacheFile)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
WorkshopFileDetailResponse localCache = null;
WorkshopFileDetailResponse steamCache = null;
await Task.Run( () => {
var file = Path.Combine(Config.Default.DataPath, Config.Default.WorkshopCacheFile);
// try to load the cache file.
localCache = WorkshopFileDetailResponse.Load(file);
if (loadFromCacheFile)
{
steamCache = localCache;
// check if the cache is old
if (localCache != null && Config.Default.WorkshopCache_ExpiredHours > 0 && localCache.cached.AddHours(Config.Default.WorkshopCache_ExpiredHours) < DateTime.UtcNow)
// cache is considered old, clear cache variable so it will reload from internet
steamCache = null;
}
// check if the cache exists
if (steamCache == null)
{
steamCache = SteamUtils.GetSteamModDetails(Config.Default.AppId);
if (steamCache != null)
steamCache.Save(file);
else
{
MessageBox.Show(_globalizer.GetResourceString("WorkshopFiles_Refresh_FailedLabel"), _globalizer.GetResourceString("WorkshopFiles_Refresh_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
steamCache = localCache;
}
}
});
WorkshopFiles = WorkshopFileList.GetList(steamCache);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
public void UpdateModDetailsList(ModDetailList modDetails)
{
if (_modDetails != null)
_modDetails.CollectionChanged -= ModDetails_CollectionChanged;
_modDetails = modDetails ?? new ModDetailList();
if (_modDetails != null)
_modDetails.CollectionChanged += ModDetails_CollectionChanged;
WorkshopFilesView?.Refresh();
}
#region Filtering
private void FilterWorkshopFiles_Click(object sender, RoutedEventArgs e)
{
WorkshopFilesView?.Refresh();
}
public bool Filter(object obj)
{
var data = obj as WorkshopFileItem;
if (data == null)
return false;
if (WorkshopFilterExisting && _modDetails.Any(m => m.ModId.Equals(data.WorkshopId)))
return false;
var filterString = WorkshopFilterString.ToLower();
if (string.IsNullOrWhiteSpace(filterString))
return true;
return data.WorkshopId.Contains(filterString) || data.TitleFilterString.Contains(filterString);
}
#endregion
}
}

View file

@ -0,0 +1,127 @@
<Window x:Class="ServerManagerTool.WorldSaveRestoreWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:com="clr-namespace:ServerManagerTool.Common;assembly=ServerManager.Common"
MinWidth="700" MinHeight="480" Width="800" Height="480" ResizeMode="CanResize" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Loaded="Window_Loaded"
Icon="../Art/favicon.ico" Title="{DynamicResource WorldSaveRestore_Title}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Globalization\en-US\en-US.xaml"/>
<ResourceDictionary Source="..\Styles\Default.xaml"/>
</ResourceDictionary.MergedDictionaries>
<LinearGradientBrush x:Key="BeigeGradient" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource BeigeGradient}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="Reload_Click" Margin="5,5,5,0" Width="22" Height="22" ToolTip="{DynamicResource WorldSaveRestore_ReloadTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Reload.ico,Size=32}" />
</Button>
<DataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding WorldSaveFiles}" Margin="5" AutoGenerateColumns="False" CanUserAddRows="False" CanUserReorderColumns="False" CanUserSortColumns="True" RowHeaderWidth="0" SelectionMode="Single">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="{x:Static SystemColors.HighlightTextColor}"/>
</Style.Resources>
<Style.Triggers>
<DataTrigger Binding="{Binding IsActiveFile}" Value="True">
<Setter Property="Background" Value="#FFF5DFB8" />
</DataTrigger>
<DataTrigger Binding="{Binding IsArchiveFile}" Value="True">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.HorizontalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.HorizontalGridLinesBrush>
<DataGrid.VerticalGridLinesBrush>
<SolidColorBrush Color="#FFB4B4B4"/>
</DataGrid.VerticalGridLinesBrush>
<DataGrid.Columns>
<DataGridTextColumn Width="1*" Binding="{Binding FileName, Mode=OneWay}" Header="{DynamicResource WorldSaveRestore_NameColumnLabel}"/>
<DataGridTextColumn Width="130" Binding="{Binding CreatedDate, Mode=OneWay}" Header="{DynamicResource WorldSaveRestore_CreatedDateColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Width="130" Binding="{Binding UpdatedDate, Mode=OneWay}" Header="{DynamicResource WorldSaveRestore_UpdatedDateColumnLabel}">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.HeaderStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Restore_Click" ToolTip="{DynamicResource WorldSaveRestore_RestoreTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Download.ico,Size=32}" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActiveFile}" Value="True">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30" CanUserReorder="False" CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="22" Height="22" Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Delete_Click" ToolTip="{DynamicResource WorldSaveRestore_DeleteTooltip}">
<Image Source="{com:Icon Path=/ConanServerManager;component/Art/Delete.ico,Size=32}" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActiveFile}" Value="True">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

View file

@ -0,0 +1,295 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Lib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for WorldSaveRestoreWindow.xaml
/// </summary>
public partial class WorldSaveRestoreWindow : Window
{
public class WorldSaveFileList : SortableObservableCollection<WorldSaveFile>
{
public new void Add(WorldSaveFile item)
{
if (item == null || this.Any(m => m.FileName.Equals(item.FileName)))
return;
base.Add(item);
}
public override string ToString()
{
return $"{nameof(WorldSaveFile)} - {Count}";
}
}
public class WorldSaveFile : DependencyObject
{
public static readonly DependencyProperty CreatedDateProperty = DependencyProperty.Register(nameof(CreatedDate), typeof(DateTime), typeof(WorldSaveFile), new PropertyMetadata(DateTime.MinValue));
public static readonly DependencyProperty FileProperty = DependencyProperty.Register(nameof(File), typeof(string), typeof(WorldSaveFile), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register(nameof(FileName), typeof(string), typeof(WorldSaveFile), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty UpdatedDateProperty = DependencyProperty.Register(nameof(UpdatedDate), typeof(DateTime), typeof(WorldSaveFile), new PropertyMetadata(DateTime.MinValue));
public static readonly DependencyProperty IsActiveFileProperty = DependencyProperty.Register(nameof(IsActiveFile), typeof(bool), typeof(WorldSaveFile), new PropertyMetadata(false));
public static readonly DependencyProperty IsArchiveFileProperty = DependencyProperty.Register(nameof(IsArchiveFile), typeof(bool), typeof(WorldSaveFile), new PropertyMetadata(false));
public DateTime CreatedDate
{
get { return (DateTime)GetValue(CreatedDateProperty); }
set { SetValue(CreatedDateProperty, value); }
}
public string File
{
get { return (string)GetValue(FileProperty); }
set { SetValue(FileProperty, value); }
}
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
public DateTime UpdatedDate
{
get { return (DateTime)GetValue(UpdatedDateProperty); }
set { SetValue(UpdatedDateProperty, value); }
}
public bool IsActiveFile
{
get { return (bool)GetValue(IsActiveFileProperty); }
set { SetValue(IsActiveFileProperty, value); }
}
public bool IsArchiveFile
{
get { return (bool)GetValue(IsArchiveFileProperty); }
set { SetValue(IsArchiveFileProperty, value); }
}
public override string ToString()
{
return FileName;
}
}
public class WorldSaveFileComparer : IComparer<WorldSaveFile>
{
public int Compare(WorldSaveFile x, WorldSaveFile y)
{
if (x == null && y == null)
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
if (x.IsActiveFile && y.IsActiveFile)
{
if (x.UpdatedDate == y.UpdatedDate)
return 0;
return x.UpdatedDate < y.UpdatedDate ? 1 : -1;
}
if (x.IsActiveFile)
return -1;
if (y.IsActiveFile)
return 1;
if (x.UpdatedDate == y.UpdatedDate)
return 0;
return x.UpdatedDate < y.UpdatedDate ? 1 : -1;
}
}
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
private readonly ServerProfile _profile = null;
public static readonly DependencyProperty WorldSaveFilesProperty = DependencyProperty.Register(nameof(WorldSaveFiles), typeof(WorldSaveFileList), typeof(WorldSaveRestoreWindow), new PropertyMetadata(null));
public WorldSaveRestoreWindow(ServerProfile profile)
{
InitializeComponent();
WindowUtils.RemoveDefaultResourceDictionary(this, Config.Default.DefaultGlobalizationFile);
_profile = profile;
this.Title = string.Format(_globalizer.GetResourceString("WorldSaveRestore_ProfileTitle"), _profile?.ProfileName);
WorldSaveFiles = new WorldSaveFileList();
this.DataContext = this;
}
public WorldSaveFileList WorldSaveFiles
{
get { return GetValue(WorldSaveFilesProperty) as WorldSaveFileList; }
set { SetValue(WorldSaveFilesProperty, value); }
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
await LoadWorldSaveFiles();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("WorldSaveRestore_Load_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async void Delete_Click(object sender, RoutedEventArgs e)
{
var item = ((WorldSaveFile)((Button)e.Source).DataContext);
if (item == null)
return;
var deleteMessage = _globalizer.GetResourceString("WorldSaveRestore_DeleteConfirmationLabel");
deleteMessage = deleteMessage.Replace("{fileName}", item.FileName);
if (MessageBox.Show(this, deleteMessage, _globalizer.GetResourceString("WorldSaveRestore_DeleteConfirmationTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
File.Delete(item.File);
MessageBox.Show(this, _globalizer.GetResourceString("WorldSaveRestore_DeleteSuccessLabel"), _globalizer.GetResourceString("WorldSaveRestore_DeleteSuccessTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, _globalizer.GetResourceString("WorldSaveRestore_DeleteErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
await LoadWorldSaveFiles();
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private async void Reload_Click(object sender, RoutedEventArgs e)
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
await LoadWorldSaveFiles();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, _globalizer.GetResourceString("WorldSaveRestore_Refresh_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private async void Restore_Click(object sender, RoutedEventArgs e)
{
var item = ((WorldSaveFile)((Button)e.Source).DataContext);
if (item == null)
return;
var confirmationMessage = _globalizer.GetResourceString("WorldSaveRestore_RestoreConfirmationLabel");
confirmationMessage = confirmationMessage.Replace("{fileName}", item.FileName);
if (MessageBox.Show(this, confirmationMessage, _globalizer.GetResourceString("WorldSaveRestore_RestoreConfirmationTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
var restoredFileCount = _profile.RestoreSaveFiles(item.File, item.IsArchiveFile);
var successMessage = _globalizer.GetResourceString("WorldSaveRestore_RestoreSuccessLabel");
successMessage = successMessage.Replace("{fileCount}", restoredFileCount.ToString());
MessageBox.Show(this, successMessage, _globalizer.GetResourceString("WorldSaveRestore_RestoreSuccessTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, _globalizer.GetResourceString("WorldSaveRestore_RestoreErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
await LoadWorldSaveFiles();
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
private async Task LoadWorldSaveFiles()
{
var cursor = this.Cursor;
try
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = System.Windows.Input.Cursors.Wait);
await Task.Delay(500);
WorldSaveFiles.Clear();
var saveFolder = ServerProfile.GetProfileSavePath(_profile);
if (!Directory.Exists(saveFolder))
return;
var saveFolderInfo = new DirectoryInfo(saveFolder);
var mapFileName = _profile.ServerMapSaveFileName;
var mapName = Path.GetFileNameWithoutExtension(mapFileName);
var mapExtension = Path.GetExtension(mapFileName);
var searchPattern = $"{mapName}{mapExtension}";
var saveFiles = saveFolderInfo.GetFiles(searchPattern);
foreach (var file in saveFiles)
{
WorldSaveFiles.Add(new WorldSaveFile { File = file.FullName, FileName = file.Name, CreatedDate = file.CreationTime, UpdatedDate = file.LastWriteTime, IsArchiveFile = false, IsActiveFile = file.Name.Equals(mapFileName, StringComparison.OrdinalIgnoreCase) });
}
searchPattern = $"{mapName}_backup_*{mapExtension}";
saveFiles = saveFolderInfo.GetFiles(searchPattern);
foreach (var file in saveFiles)
{
WorldSaveFiles.Add(new WorldSaveFile { File = file.FullName, FileName = file.Name, CreatedDate = file.CreationTime, UpdatedDate = file.LastWriteTime, IsArchiveFile = false, IsActiveFile = file.Name.Equals(mapFileName, StringComparison.OrdinalIgnoreCase) });
}
var backupFolder = ServerApp.GetServerBackupFolder(_profile);
if (Directory.Exists(backupFolder))
{
var backupFolderInfo = new DirectoryInfo(backupFolder);
searchPattern = $"{mapName}*{Config.Default.BackupExtension}";
var backupFiles = backupFolderInfo.GetFiles(searchPattern);
foreach (var file in backupFiles)
{
WorldSaveFiles.Add(new WorldSaveFile { File = file.FullName, FileName = file.Name, CreatedDate = file.CreationTime, UpdatedDate = file.LastWriteTime, IsArchiveFile = true, IsActiveFile = false });
}
}
WorldSaveFiles.Sort(f => f, new WorldSaveFileComparer());
}
finally
{
Application.Current.Dispatcher.Invoke(() => this.Cursor = cursor);
}
}
}
}