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,557 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="ServerManagerTool.Common.CommonConfig" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
<section name="ServerManagerTool.Config" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
</sectionGroup>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="ServerManagerTool.Config" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup>
<applicationSettings>
<ServerManagerTool.Config>
<setting name="SteamCmdInstallServerArgsFormat" serializeAs="String">
<value>+login {0} +force_install_dir "{1}" "+app_update {2} {3} {4}" +quit</value>
</setting>
<setting name="ServersInstallPath" serializeAs="String">
<value>Servers</value>
</setting>
<setting name="DefaultServerProfileName" serializeAs="String">
<value>Unnamed Profile</value>
</setting>
<setting name="DefaultServerName" serializeAs="String">
<value>Unnamed Server</value>
</setting>
<setting name="ServerBinaryRelativePath" serializeAs="String">
<value>ConanSandbox\Binaries\Win64</value>
</setting>
<setting name="ServerConfigRelativePath" serializeAs="String">
<value>ConanSandbox\Saved\Config\WindowsServer</value>
</setting>
<setting name="ServerExeFile" serializeAs="String">
<value>ConanSandboxServer-Win64-Test.exe</value>
</setting>
<setting name="ProfilesRelativePath" serializeAs="String">
<value>profiles</value>
</setting>
<setting name="ProfileExtension" serializeAs="String">
<value>.profile</value>
</setting>
<setting name="ServerProcessName" serializeAs="String">
<value>ConanSandboxServer-Win64-Test</value>
</setting>
<setting name="LoadProfileExtensionList" serializeAs="String">
<value>*.profile,*.ini</value>
</setting>
<setting name="LogsRelativePath" serializeAs="String">
<value>Logs</value>
</setting>
<setting name="LatestServerManagerVersionUrl" serializeAs="String">
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/latest.txt</value>
</setting>
<setting name="UpdateCheckTime" serializeAs="String">
<value>1440</value>
</setting>
<setting name="SavedFilesRelativePath" serializeAs="String">
<value>ConanSandbox\Saved</value>
</setting>
<setting name="HelpUrl" serializeAs="String">
<value>http://servermanagers.freeforums.net/thread/43/faq-frequently-asked</value>
</setting>
<setting name="AppPatchNotesUrl" serializeAs="String">
<value>https://forums.funcom.com/c/conan-exiles/Patch-Notes</value>
</setting>
<setting name="AppUrl" serializeAs="String">
<value>http://store.steampowered.com/app/440900/</value>
</setting>
<setting name="LastUpdatedTimeFile" serializeAs="String">
<value>LastUpdatedSM.txt</value>
</setting>
<setting name="LauncherFile" serializeAs="String">
<value>RunServer.cmd</value>
</setting>
<setting name="SteamCmdInstallModArgsFormat" serializeAs="String">
<value>+login {0} +workshop_download_item {1} {2} +quit</value>
</setting>
<setting name="AppSteamWorkshopFolderRelativePath" serializeAs="String">
<value>steamapps\workshop\content\440900\</value>
</setting>
<setting name="ServerModsRelativePath" serializeAs="String">
<value>ConanSandbox\Mods</value>
</setting>
<setting name="SavedRelativePath" serializeAs="String">
<value>Saved</value>
</setting>
<setting name="WorkshopCacheFile" serializeAs="String">
<value>workshopcache_440900.json</value>
</setting>
<setting name="SteamCmd_AnonymousUsername" serializeAs="String">
<value>anonymous</value>
</setting>
<setting name="SteamWorkshopFolderRelativePath" serializeAs="String">
<value>steamapps\workshop</value>
</setting>
<setting name="AppSteamWorkshopFile" serializeAs="String">
<value>appworkshop_440900.acf</value>
</setting>
<setting name="AppId" serializeAs="String">
<value>440900</value>
</setting>
<setting name="AppIdServer" serializeAs="String">
<value>443030</value>
</setting>
<setting name="ServerAppIdFile" serializeAs="String">
<value>steam_appid.txt</value>
</setting>
<setting name="LatestServerManagerBetaVersionUrl" serializeAs="String">
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/beta/latest.txt</value>
</setting>
<setting name="LatestServerManagerBetaDownloadUrl" serializeAs="String">
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/beta/latest.zip</value>
</setting>
<setting name="LatestServerManagerDownloadUrl" serializeAs="String">
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/latest.zip</value>
</setting>
<setting name="LatestServerManagerPatchNotesUrl" serializeAs="String">
<value>http://servermanagers.freeforums.net/thread/36/downloads</value>
</setting>
<setting name="LatestServerManagerBetaPatchNotesUrl" serializeAs="String">
<value>http://servermanagers.freeforums.net/board/44/beta-testers</value>
</setting>
<setting name="ServerBlacklistFile" serializeAs="String">
<value>blacklist.txt</value>
</setting>
<setting name="DefaultServerRelativePath" serializeAs="String">
<value>server</value>
</setting>
<setting name="ServerStatusUrlFormat" serializeAs="String">
<value />
</setting>
<setting name="BackupRelativePath" serializeAs="String">
<value>_backup_</value>
</setting>
<setting name="ServerGameConfigFile" serializeAs="String">
<value>Game.ini</value>
</setting>
<setting name="SteamWebAPIKeyHelpUrl" serializeAs="String">
<value>http://servermanagers.freeforums.net/thread/40/get-own-steam-web-api</value>
</setting>
<setting name="BackupServerExtension" serializeAs="String">
<value>.smbak</value>
</setting>
<setting name="ScheduledTasksCheckTime" serializeAs="String">
<value>1</value>
</setting>
<setting name="ServerManagerPluginUrl" serializeAs="String">
<value>http://servermanagers.freeforums.net/board/36/plugins</value>
</setting>
<setting name="ServerManagerForumUrl" serializeAs="String">
<value>http://servermanagers.freeforums.net/</value>
</setting>
<setting name="DefaultGlobalizationFile" serializeAs="String">
<value>Globalization\en-US\en-US.xaml</value>
</setting>
<setting name="ScheduledTaskFolder" serializeAs="String">
<value>ConanServerManager</value>
</setting>
<setting name="FirewallRulePrefix" serializeAs="String">
<value>ConanServer:</value>
</setting>
<setting name="DonationUrl" serializeAs="String">
<value>https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=bletch1971%40hotmail%2ecom&amp;lc=US&amp;item_name=Server%20Manager&amp;currency_code=USD&amp;bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted</value>
</setting>
<setting name="ServerManagerCode" serializeAs="String">
<value>F2653C3D-BC83-440A-AD99-FD9D9466DE04</value>
</setting>
<setting name="BackupExtension" serializeAs="String">
<value>.zip</value>
</setting>
<setting name="UpdaterPrefix" serializeAs="String">
<value>Conan_</value>
</setting>
<setting name="UpdaterFile" serializeAs="String">
<value>ServerManagerUpdater.exe</value>
</setting>
<setting name="ServerCommandLineArgsIPMatchFormat" serializeAs="String">
<value>-MultiHome={0}</value>
</setting>
<setting name="ServerCommandLineArgsPortMatchFormat" serializeAs="String">
<value>-QueryPort={0}</value>
</setting>
<setting name="GameDataRelativePath" serializeAs="String">
<value>GameData</value>
</setting>
<setting name="GameDataExtension" serializeAs="String">
<value>.gamedata</value>
</setting>
<setting name="GameDataApplication" serializeAs="String">
<value>conan</value>
</setting>
<setting name="ServerManagerVersionFeedUrl" serializeAs="String">
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/VersionFeed.xml</value>
</setting>
<setting name="ServerManagerVersionBetaFeedUrl" serializeAs="String">
<value>https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/beta/VersionFeed.xml</value>
</setting>
<setting name="DefaultServerBranchName" serializeAs="String">
<value>live</value>
</setting>
<setting name="SteamCmdInstallServerBetaNameArgsFormat" serializeAs="String">
<value>-beta {0}</value>
</setting>
<setting name="SteamCmdInstallServerBetaPasswordArgsFormat" serializeAs="String">
<value>-betapassword {0}</value>
</setting>
<setting name="ServerBranchFolderPrefix" serializeAs="String">
<value>__</value>
</setting>
<setting name="AppSteamManifestFile" serializeAs="String">
<value>appmanifest_443030.acf</value>
</setting>
<setting name="SteamManifestFolderRelativePath" serializeAs="String">
<value>steamapps</value>
</setting>
<setting name="ServerEngineConfigFile" serializeAs="String">
<value>Engine.ini</value>
</setting>
<setting name="ServerSettingsConfigFile" serializeAs="String">
<value>ServerSettings.ini</value>
</setting>
<setting name="SendMessageDelay" serializeAs="String">
<value>5000</value>
</setting>
<setting name="PlayerImageFileExtension" serializeAs="String">
<value>.png</value>
</setting>
<setting name="ServerShutdownCommand" serializeAs="String">
<value>doexit</value>
</setting>
<setting name="ServerSaveCommand" serializeAs="String">
<value>saveworld</value>
</setting>
<setting name="DefaultStyleFile" serializeAs="String">
<value>Styles\Default.xaml</value>
</setting>
<setting name="ServerWhitelistFile" serializeAs="String">
<value>whitelist.txt</value>
</setting>
<setting name="ServerCommandLineStandardArgs" serializeAs="String">
<value>-listen -nosteamclient -game -server -log</value>
</setting>
<setting name="GameDataUrl" serializeAs="String">
<value>https://servermanagers.freeforums.net/</value>
</setting>
<setting name="ServerStatusWatcher_LocalStatusQueryDelay" serializeAs="String">
<value>15000</value>
</setting>
<setting name="ServerStatusWatcher_RemoteStatusQueryDelay" serializeAs="String">
<value>120000</value>
</setting>
</ServerManagerTool.Config>
<ServerManagerTool.Common.CommonConfig>
<setting name="DefaultSteamAPIKey" serializeAs="String">
<value>2043D84BBC372F2F47D9AF937129D9C2</value>
</setting>
</ServerManagerTool.Common.CommonConfig>
</applicationSettings>
<userSettings>
<ServerManagerTool.Config>
<setting name="ConfigPath" serializeAs="String">
<value />
</setting>
<setting name="DataPath" serializeAs="String">
<value />
</setting>
<setting name="MachinePublicIP" serializeAs="String">
<value />
</setting>
<setting name="ManageFirewallAutomatically" serializeAs="String">
<value>True</value>
</setting>
<setting name="UpgradeConfig" serializeAs="String">
<value>True</value>
</setting>
<setting name="CultureName" serializeAs="String">
<value>en-US</value>
</setting>
<setting name="SectionAdministrationIsExpanded" serializeAs="String">
<value>False</value>
</setting>
<setting name="SectionAutomaticManagementIsExpanded" serializeAs="String">
<value>False</value>
</setting>
<setting name="RunAsAdministratorPrompt" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoUpdate_EnableUpdate" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoUpdate_UpdatePeriod" serializeAs="String">
<value>60</value>
</setting>
<setting name="ServerShutdown_GracePeriod" serializeAs="String">
<value>15</value>
</setting>
<setting name="ServerShutdown_GraceMessage1" serializeAs="String">
<value>Server shutdown required. Server will shutdown in {minutes} minutes. Please logout before shutdown to prevent character corruption.</value>
</setting>
<setting name="ServerShutdown_GraceMessage2" serializeAs="String">
<value>Server shutdown required. Server will shutdown in 1 minute. Please logout before shutdown to prevent character corruption.</value>
</setting>
<setting name="ServerShutdown_GraceMessage3" serializeAs="String">
<value>Server shutdown required. Server is shutting down now.</value>
</setting>
<setting name="ServerShutdown_WorldSaveMessage" serializeAs="String">
<value>Server shutdown required. Server is about to shutdown, performing a world save.</value>
</setting>
<setting name="ServerShutdown_EnableWorldSave" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoUpdate_CacheDir" serializeAs="String">
<value />
</setting>
<setting name="ServerUpdate_UpdateModsWhenUpdatingServer" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerUpdate_ForceUpdateMods" serializeAs="String">
<value>False</value>
</setting>
<setting name="ServerUpdate_ForceCopyMods" serializeAs="String">
<value>False</value>
</setting>
<setting name="SteamCmdRedirectOutput" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoUpdate_UseSmartCopy" serializeAs="String">
<value>True</value>
</setting>
<setting name="StyleName" serializeAs="String">
<value />
</setting>
<setting name="Email_Host" serializeAs="String">
<value />
</setting>
<setting name="Email_Port" serializeAs="String">
<value>25</value>
</setting>
<setting name="Email_UseSSL" serializeAs="String">
<value>False</value>
</setting>
<setting name="Email_UseDetaultCredentials" serializeAs="String">
<value>False</value>
</setting>
<setting name="Email_Username" serializeAs="String">
<value />
</setting>
<setting name="Email_Password" serializeAs="String">
<value />
</setting>
<setting name="Email_From" serializeAs="String">
<value />
</setting>
<setting name="Email_To" serializeAs="String">
<value />
</setting>
<setting name="EmailNotify_AutoUpdate" serializeAs="String">
<value>False</value>
</setting>
<setting name="EmailNotify_AutoRestart" serializeAs="String">
<value>False</value>
</setting>
<setting name="SteamCmd_Username" serializeAs="String">
<value />
</setting>
<setting name="SteamCmd_UseAnonymousCredentials" serializeAs="String">
<value>True</value>
</setting>
<setting name="SteamCmd_Password" serializeAs="String">
<value />
</setting>
<setting name="ValidateProfileOnServerStart" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerUpdate_OnServerStart" serializeAs="String">
<value>False</value>
</setting>
<setting name="SectionServerFilesIsExpanded" serializeAs="String">
<value>False</value>
</setting>
<setting name="EmailNotify_ShutdownRestart" serializeAs="String">
<value>False</value>
</setting>
<setting name="WorkshopCache_ExpiredHours" serializeAs="String">
<value>168</value>
</setting>
<setting name="ServerShutdown_CancelMessage" serializeAs="String">
<value>Server shutdown has been cancelled.</value>
</setting>
<setting name="ServerUpdate_ForceUpdateModsIfNoSteamInfo" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoBackup_EnableBackup" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoBackup_BackupPeriod" serializeAs="String">
<value>60</value>
</setting>
<setting name="EmailNotify_AutoBackup" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoBackup_DeleteOldFiles" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoBackup_DeleteInterval" serializeAs="String">
<value>30</value>
</setting>
<setting name="ServerBackup_WorldSaveMessage" serializeAs="String">
<value>A world save is about to be performed, you may experience some lag during this process. Please be patient.</value>
</setting>
<setting name="AutoUpdate_RetryOnFail" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoUpdate_ShowUpdateReason" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoUpdate_ValidateServerFiles" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoUpdate_OverrideServerStartup" serializeAs="String">
<value>False</value>
</setting>
<setting name="ServerManagerUniqueKey" serializeAs="String">
<value />
</setting>
<setting name="AutoUpdate_ParallelUpdate" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoUpdate_UpdateReasonPrefix" serializeAs="String">
<value>Server Update Reason:</value>
</setting>
<setting name="Alert_ServerStopMessage" serializeAs="String">
<value>The server is stopping.</value>
</setting>
<setting name="Alert_ServerShutdownMessage" serializeAs="String">
<value>The server is shutting down.</value>
</setting>
<setting name="Alert_ServerStartedMessage" serializeAs="String">
<value>The server is starting.</value>
</setting>
<setting name="Alert_BackupProcessError" serializeAs="String">
<value>The server backup process was performed but an error occurred.</value>
</setting>
<setting name="Alert_ShutdownProcessError" serializeAs="String">
<value>The server shutdown process was performed but an error occurred.</value>
</setting>
<setting name="Alert_RestartProcessError" serializeAs="String">
<value>The server restart process was performed but an error occurred.</value>
</setting>
<setting name="Alert_UpdateProcessError" serializeAs="String">
<value>The server update process was performed but an error occurred.</value>
</setting>
<setting name="Alert_ModUpdateDetected" serializeAs="String">
<value>Mod updates have been detected:</value>
</setting>
<setting name="ServerShutdown_WorldSaveDelay" serializeAs="String">
<value>10</value>
</setting>
<setting name="ServerShutdown_UseShutdownCommand" serializeAs="String">
<value>False</value>
</setting>
<setting name="Alert_ServerStatusChange" serializeAs="String">
<value>Server Status:</value>
</setting>
<setting name="Alert_ServerUpdate" serializeAs="String">
<value>Game Server Update</value>
</setting>
<setting name="Alert_UpdateResults" serializeAs="String">
<value>Update performed, includes:</value>
</setting>
<setting name="BackupPath" serializeAs="String">
<value />
</setting>
<setting name="ServerShutdown_AllMessagesShowReason" serializeAs="String">
<value>False</value>
</setting>
<setting name="CloseShutdownWindowWhenFinished" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerModListFile" serializeAs="String">
<value>modlist.txt</value>
</setting>
<setting name="RCON_PlayerListFilter" serializeAs="String">
<value>19</value>
</setting>
<setting name="RCON_PlayerListSort" serializeAs="String">
<value>1</value>
</setting>
<setting name="PlayerListFilter" serializeAs="String">
<value>19</value>
</setting>
<setting name="PlayerListSort" serializeAs="String">
<value>1</value>
</setting>
<setting name="ServerStatus_EnableActions" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerStatus_ShowActionConfirmation" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoUpdate_SequencialDelayPeriod" serializeAs="String">
<value>0</value>
</setting>
<setting name="BackupWorldFile" serializeAs="String">
<value>True</value>
</setting>
<setting name="MainWindow_Width" serializeAs="String">
<value>1100</value>
</setting>
<setting name="MainWindow_Height" serializeAs="String">
<value>900</value>
</setting>
<setting name="ServerMonitorWindow_Width" serializeAs="String">
<value>900</value>
</setting>
<setting name="ServerMonitorWindow_Height" serializeAs="String">
<value>500</value>
</setting>
<setting name="UpdateDirectoryPermissions" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoUpdate_VerifyServerAfterUpdate" serializeAs="String">
<value>False</value>
</setting>
<setting name="ServerShutdown_CheckForOnlinePlayers" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerShutdown_SendShutdownMessages" serializeAs="String">
<value>True</value>
</setting>
<setting name="AutoRestart_EnabledGracePeriod" serializeAs="String">
<value>False</value>
</setting>
<setting name="AutoRestart_GracePeriod" serializeAs="String">
<value>0</value>
</setting>
<setting name="MainWindow_MinimizeToTray" serializeAs="String">
<value>False</value>
</setting>
<setting name="ServerMonitorMessageOutput_Height" serializeAs="String">
<value>100</value>
</setting>
<setting name="Alert_ServerStartedMessageIncludeIPandPort" serializeAs="String">
<value>True</value>
</setting>
<setting name="ManagePublicIPAutomatically" serializeAs="String">
<value>True</value>
</setting>
</ServerManagerTool.Config>
</userSettings>
</configuration>

View file

@ -0,0 +1,7 @@
<globalizer:GlobalizedApplication
x:Class="ServerManagerTool.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:globalizer="clr-namespace:WPFSharp.Globalizer;assembly=WPFSharp.Globalizer"
StartupUri="Windows/AutoUpdateWindow.xaml">
</globalizer:GlobalizedApplication>

View file

@ -0,0 +1,530 @@
using Microsoft.WindowsAPICodePack.Dialogs;
using NLog;
using NLog.Config;
using NLog.Targets;
using ServerManagerTool.Common;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib;
using ServerManagerTool.Plugin.Common;
using ServerManagerTool.Windows;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : GlobalizedApplication, INotifyPropertyChanged
{
public new static App Instance
{
get;
private set;
}
public event PropertyChangedEventHandler PropertyChanged;
private GlobalizedApplication _globalizer;
private bool _applicationStarted;
private string _args;
private bool _betaVersion;
private string _title;
private string _version;
public App()
{
if (string.IsNullOrWhiteSpace(Config.Default.ServerManagerUniqueKey))
Config.Default.ServerManagerUniqueKey = Guid.NewGuid().ToString();
App.Instance = this;
ApplicationStarted = false;
Args = string.Empty;
BetaVersion = false;
Title = string.Empty;
Version = AppUtils.GetDeployedVersion(Assembly.GetEntryAssembly());
AppDomain.CurrentDomain.UnhandledException += ErrorHandling.CurrentDomain_UnhandledException;
MigrateSettings();
ReconfigureLogging();
}
public bool ApplicationStarted
{
get
{
return _applicationStarted;
}
set
{
if (!Equals(value, _applicationStarted))
{
_applicationStarted = value;
OnPropertyChanged();
}
}
}
public string Args
{
get
{
return _args;
}
set
{
if (!Equals(value, _args))
{
_args = value;
OnPropertyChanged();
}
}
}
public bool BetaVersion
{
get
{
return _betaVersion;
}
set
{
if (!Equals(value, _betaVersion))
{
_betaVersion = value;
OnPropertyChanged();
}
}
}
public string Title
{
get
{
return _title;
}
set
{
if (!Equals(value, _title))
{
_title = value;
OnPropertyChanged();
}
}
}
public string Version
{
get
{
return _version;
}
set
{
if (!Equals(value, _version))
{
_version = value;
OnPropertyChanged();
}
}
}
public static void DiscoverMachinePublicIP(bool forceOverride)
{
if (forceOverride || string.IsNullOrWhiteSpace(Config.Default.MachinePublicIP))
{
var publicIP = NetworkUtils.DiscoverPublicIP();
if (string.IsNullOrWhiteSpace(publicIP))
return;
if (!Config.Default.MachinePublicIP.Equals(publicIP, StringComparison.OrdinalIgnoreCase))
{
Config.Default.MachinePublicIP = publicIP;
}
}
}
public static async Task DiscoverMachinePublicIPAsync(bool forceOverride)
{
if (forceOverride || string.IsNullOrWhiteSpace(Config.Default.MachinePublicIP))
{
var publicIP = await NetworkUtils.DiscoverPublicIPAsync();
if (string.IsNullOrWhiteSpace(publicIP))
return;
if (!Config.Default.MachinePublicIP.Equals(publicIP, StringComparison.OrdinalIgnoreCase))
{
await App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
Config.Default.MachinePublicIP = publicIP;
}));
}
}
}
private IList<Plugin.Common.Lib.Profile> FetchProfiles()
{
return ServerManager.Instance.Servers.Select(s => new ServerManagerTool.Plugin.Common.Lib.Profile() { ProfileName = s?.Profile?.ProfileName ?? string.Empty, InstallationFolder = s?.Profile?.InstallDirectory ?? string.Empty }).ToList();
}
public static string GetLogFolder() => IOUtils.NormalizePath(Path.Combine(Config.Default.DataPath, Config.Default.LogsRelativePath));
public static string GetProfileLogFolder(string profileId) => IOUtils.NormalizePath(Path.Combine(Config.Default.DataPath, Config.Default.LogsRelativePath, profileId.ToLower()));
public static Logger GetProfileLogger(string profileId, string name, LogLevel minLevel, LogLevel maxLevel)
{
if (string.IsNullOrWhiteSpace(profileId) || string.IsNullOrWhiteSpace(name))
return null;
var loggerName = $"{profileId.ToLower()}_{name}".Replace(" ", "_");
if (LogManager.Configuration.FindTargetByName(loggerName) == null)
{
var logFilePath = GetProfileLogFolder(profileId);
if (!System.IO.Directory.Exists(logFilePath))
System.IO.Directory.CreateDirectory(logFilePath);
var logFile = new FileTarget(loggerName)
{
FileName = Path.Combine(logFilePath, $"{name}.log"),
Layout = "${time} ${message}",
ArchiveFileName = Path.Combine(logFilePath, $"{name}.{{#}}.log"),
ArchiveNumbering = ArchiveNumberingMode.DateAndSequence,
ArchiveEvery = FileArchivePeriod.Day,
ArchiveDateFormat = "yyyyMMdd"
};
LogManager.Configuration.AddTarget(loggerName, logFile);
var rule = new LoggingRule(loggerName, minLevel, maxLevel, logFile);
LogManager.Configuration.LoggingRules.Add(rule);
LogManager.ReconfigExistingLoggers();
}
return LogManager.GetLogger(loggerName);
}
private static void MigrateSettings()
{
var installFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
//
// Migrate settings when we update.
//
if (CommonConfig.Default.UpgradeConfig)
{
var settingsFile = IOUtils.NormalizePath(Path.Combine(installFolder, "commonconfig.json"));
CommonConfig.Default.Upgrade();
CommonConfig.Default.Reload();
SettingsUtils.MigrateSettings(CommonConfig.Default, settingsFile);
CommonConfig.Default.UpgradeConfig = false;
CommonConfig.Default.Save();
}
if (Config.Default.UpgradeConfig)
{
var settingsFile = IOUtils.NormalizePath(Path.Combine(installFolder, "userconfig.json"));
Config.Default.Upgrade();
Config.Default.Reload();
SettingsUtils.MigrateSettings(Config.Default, settingsFile);
Config.Default.UpgradeConfig = false;
Config.Default.Save();
}
Config.Default.SteamCmdRedirectOutput = false;
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
_globalizer = GlobalizedApplication.Instance;
try
{
if (!string.IsNullOrWhiteSpace(Config.Default.CultureName))
_globalizer.GlobalizationManager.SwitchLanguage(Config.Default.CultureName, true);
}
catch (Exception ex)
{
// just output the exception message, it should default back to the fallback language.
Debug.WriteLine(ex.Message);
}
try
{
if (!string.IsNullOrWhiteSpace(Config.Default.StyleName))
_globalizer.StyleManager.SwitchStyle(Config.Default.StyleName, true);
}
catch (Exception ex)
{
// just output the exception message, it should default back to the fallback style.
Debug.WriteLine(ex.Message);
}
TaskSchedulerUtils.TaskFolder = Config.Default.ScheduledTaskFolder;
this.Args = string.Join(" ", e.Args);
// check if we are starting server manager in BETA/TEST mode
if (e.Args.Any(a => a.Equals(Constants.ARG_BETA, StringComparison.OrdinalIgnoreCase) || a.Equals(Constants.ARG_TEST, StringComparison.OrdinalIgnoreCase)))
{
this.BetaVersion = true;
}
// check if we need to set the title
if (e.Args.Any(a => a.Equals(Constants.ARG_TITLE, StringComparison.OrdinalIgnoreCase)))
{
for (int i = 0; i < e.Args.Length - 1; i++)
{
if (e.Args[i].Equals(Constants.ARG_TITLE, StringComparison.OrdinalIgnoreCase) && i < e.Args.Length - 1 && !e.Args[i + 1].StartsWith("-"))
{
Title = e.Args[i + 1].Trim();
}
}
}
// check and update the public IP address
DiscoverMachinePublicIP(Config.Default.ManagePublicIPAutomatically);
var installPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
PluginHelper.Instance.BetaEnabled = this.BetaVersion;
PluginHelper.Instance.LoadPlugins(installPath, true);
PluginHelper.Instance.SetFetchProfileCallback(FetchProfiles);
// check if we are starting server manager for server shutdown
if (e.Args.Any(a => a.StartsWith(Constants.ARG_AUTOSHUTDOWN1, StringComparison.OrdinalIgnoreCase)))
{
var arg = e.Args.FirstOrDefault(a => a.StartsWith(Constants.ARG_AUTOSHUTDOWN1, StringComparison.OrdinalIgnoreCase));
var exitCode = ServerApp.PerformAutoShutdown(arg, ServerProcessType.AutoShutdown1);
// once we are finished, just exit
Environment.Exit(exitCode);
}
// check if we are starting server manager for server shutdown
if (e.Args.Any(a => a.StartsWith(Constants.ARG_AUTOSHUTDOWN2, StringComparison.OrdinalIgnoreCase)))
{
var arg = e.Args.FirstOrDefault(a => a.StartsWith(Constants.ARG_AUTOSHUTDOWN2, StringComparison.OrdinalIgnoreCase));
var exitCode = ServerApp.PerformAutoShutdown(arg, ServerProcessType.AutoShutdown2);
// once we are finished, just exit
Environment.Exit(exitCode);
}
// check if we are starting server manager for server updating
if (e.Args.Any(a => a.Equals(Constants.ARG_AUTOUPDATE, StringComparison.OrdinalIgnoreCase)))
{
var exitCode = ServerApp.PerformAutoUpdate();
// once we are finished, just exit
Environment.Exit(exitCode);
}
// check if we are starting server manager for server backups
if (e.Args.Any(a => a.Equals(Constants.ARG_AUTOBACKUP, StringComparison.OrdinalIgnoreCase)))
{
var exitCode = ServerApp.PerformAutoBackup();
// once we are finished, just exit
Environment.Exit(exitCode);
}
if (Config.Default.RunAsAdministratorPrompt && !SecurityUtils.IsAdministrator())
{
var result = MessageBox.Show(_globalizer.GetResourceString("Application_RunAsAdministratorLabel"), _globalizer.GetResourceString("Application_RunAsAdministratorTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
var processInfo = new ProcessStartInfo(Assembly.GetEntryAssembly().CodeBase);
// The following properties run the new process as administrator
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
processInfo.Arguments = string.Join(" ", e.Args);
// Start the new process
try
{
Process.Start(processInfo);
// Shut down the current process
Application.Current.Shutdown(0);
return;
}
catch (Exception)
{
// The user did not allow the application to run as administrator
MessageBox.Show(_globalizer.GetResourceString("Application_RunAsAdministrator_FailedLabel"), _globalizer.GetResourceString("Application_RunAsAdministrator_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
// check if application is already running
if (ProcessUtils.IsAlreadyRunning())
{
var result = MessageBox.Show(_globalizer.GetResourceString("Application_SingleInstanceLabel"), _globalizer.GetResourceString("Application_SingleInstanceTitle"), MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
if (ProcessUtils.SwitchToCurrentInstance())
{
// Shut down the current process
Application.Current.Shutdown(0);
return;
}
MessageBox.Show(_globalizer.GetResourceString("Application_SingleInstance_FailedLabel"), _globalizer.GetResourceString("Application_SingleInstance_FailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
this.ApplicationStarted = true;
// Initial configuration setting
if (String.IsNullOrWhiteSpace(Config.Default.DataPath))
{
MessageBox.Show(_globalizer.GetResourceString("Application_DataDirectoryLabel"), _globalizer.GetResourceString("Application_DataDirectoryTitle"), MessageBoxButton.OK, MessageBoxImage.Information);
var installationFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
if (!installationFolder.EndsWith(@"\"))
installationFolder += @"\";
while (String.IsNullOrWhiteSpace(Config.Default.DataPath))
{
var dialog = new CommonOpenFileDialog
{
EnsureFileExists = true,
IsFolderPicker = true,
Multiselect = false,
Title = _globalizer.GetResourceString("Application_DataDirectory_DialogTitle"),
InitialDirectory = installationFolder
};
if (dialog.ShowDialog() != CommonFileDialogResult.Ok)
{
Environment.Exit(0);
}
MessageBoxResult confirm = MessageBoxResult.Cancel;
// check if the folder is under the installation folder
var newDataFolder = dialog.FileName;
if (!newDataFolder.EndsWith(@"\"))
newDataFolder += @"\";
if (newDataFolder.StartsWith(installationFolder))
{
confirm = MessageBoxResult.No;
MessageBox.Show(_globalizer.GetResourceString("Application_DataDirectory_WithinInstallFolderErrorLabel"), _globalizer.GetResourceString("Application_DataDirectory_WithinInstallFolderErrorTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
confirm = MessageBox.Show(String.Format(_globalizer.GetResourceString("Application_DataDirectory_ConfirmLabel"), Path.Combine(newDataFolder, Config.Default.ProfilesRelativePath), Path.Combine(newDataFolder, CommonConfig.Default.SteamCmdRelativePath)), _globalizer.GetResourceString("Application_DataDirectory_ConfirmTitle"), MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
}
if (confirm == MessageBoxResult.Cancel)
{
Environment.Exit(0);
}
else if (confirm == MessageBoxResult.Yes)
{
if (newDataFolder.EndsWith(@"\"))
newDataFolder = newDataFolder.Substring(0, newDataFolder.Length - 1);
Config.Default.DataPath = newDataFolder;
ReconfigureLogging();
break;
}
}
}
Config.Default.ConfigPath = Path.Combine(Config.Default.DataPath, Config.Default.ProfilesRelativePath);
System.IO.Directory.CreateDirectory(Config.Default.ConfigPath);
Config.Default.Save();
CommonConfig.Default.Save();
if (e.Args.Any(a => a.StartsWith(Constants.ARG_SERVERMONITOR, StringComparison.OrdinalIgnoreCase)))
{
ServerRuntime.EnableUpdateModStatus = false;
ServerProfile.EnableServerFilesWatcher = false;
StartupUri = new Uri("Windows/ServerMonitorWindow.xaml", UriKind.RelativeOrAbsolute);
}
else
{
// initialize all the game data
GameData.Initialize();
StartupUri = new Uri("Windows/AutoUpdateWindow.xaml", UriKind.RelativeOrAbsolute);
}
}
protected override void OnExit(ExitEventArgs e)
{
ShutDownApplication();
base.OnExit(e);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static void ReconfigureLogging()
{
string logDir = Path.Combine(Config.Default.DataPath, Config.Default.LogsRelativePath);
if (!System.IO.Directory.Exists(logDir))
System.IO.Directory.CreateDirectory(logDir);
LogManager.Configuration.Variables["logDir"] = logDir;
var fileTargets = LogManager.Configuration.AllTargets.OfType<FileTarget>();
foreach (var fileTarget in fileTargets)
{
var fileName = Path.GetFileNameWithoutExtension(fileTarget.FileName.ToString());
fileTarget.FileName = Path.Combine(logDir, $"{fileName}.log");
fileTarget.ArchiveFileName = Path.Combine(logDir, $"{fileName}.{{#}}.log");
}
LogManager.ReconfigExistingLoggers();
}
private void ShutDownApplication()
{
if (this.ApplicationStarted)
{
foreach (var server in ServerManager.Instance.Servers)
{
try
{
server.Profile.Save(false, false, null);
}
catch (Exception ex)
{
MessageBox.Show(String.Format(_globalizer.GetResourceString("Application_Profile_SaveFailedLabel"), server.Profile.ProfileName, ex.Message, ex.StackTrace), _globalizer.GetResourceString("Application_Profile_SaveFailedTitle"), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
Config.Default.Save();
CommonConfig.Default.Save();
}
PluginHelper.Instance?.Dispose();
this.ApplicationStarted = false;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,593 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F7A82C6A-32CD-4847-AD16-D79ADC72746E}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>ServerManagerTool</RootNamespace>
<AssemblyName>ConanServerManager</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<IsWebBootstrapper>false</IsWebBootstrapper>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<PublishUrl>publish\</PublishUrl>
<Install>false</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>false</MapFileExtensions>
<SupportUrl>https://servermanagers.freeforums.net/thread/43/faq-frequently-asked</SupportUrl>
<ErrorReportUrl>https://servermanagers.freeforums.net/board/39/tech-support-bug-reports</ErrorReportUrl>
<ProductName>Conan Exiles™ Server Manager</ProductName>
<PublisherName>Bletch1971</PublisherName>
<SuiteName>Server Managers</SuiteName>
<OpenBrowserOnPublish>false</OpenBrowserOnPublish>
<ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>1.1.51.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject>ServerManagerTool.App</StartupObject>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup>
<TargetZone>LocalIntranet</TargetZone>
</PropertyGroup>
<PropertyGroup>
<GenerateManifests>true</GenerateManifests>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Art\favicon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>C1A6DBBCC8495EE1062FD4FB1B05D0F1D7E2E392</ManifestCertificateThumbprint>
</PropertyGroup>
<PropertyGroup>
<ManifestKeyFile>ConanServerManager_TemporaryKey.pfx</ManifestKeyFile>
</PropertyGroup>
<PropertyGroup>
<SignManifests>false</SignManifests>
</PropertyGroup>
<ItemGroup>
<Reference Include="DotNetZip, Version=1.13.8.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.13.8\lib\net40\DotNetZip.dll</HintPath>
</Reference>
<Reference Include="EO.Wpf, Version=4.0.12.0, Culture=neutral, PublicKeyToken=e92353a6bf73fffc, processorArchitecture=MSIL">
<HintPath>..\packages\EO.Wpf.4.0.12\lib\EO.Wpf.dll</HintPath>
</Reference>
<Reference Include="Hardcodet.Wpf.TaskbarNotification, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Hardcodet.NotifyIcon.Wpf.1.0.8\lib\net451\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.TaskScheduler, Version=2.8.19.0, Culture=neutral, PublicKeyToken=c416bc1b32d97233, processorArchitecture=MSIL">
<HintPath>..\packages\TaskScheduler.2.8.19\lib\net452\Microsoft.Win32.TaskScheduler.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsAPICodePack-Core.1.1.2\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.7.2\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.112.1, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\packages\System.Data.SQLite.Core.1.0.112.1\lib\net40\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Management" />
<Reference Include="System.Numerics" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Dataflow, Version=4.5.24.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll</HintPath>
</Reference>
<Reference Include="System.Transactions" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Expression.Blend.Sdk.1.0.2\lib\net45\System.Windows.Interactivity.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Lib\Model\PlayerListParameters.cs" />
<Compile Include="Lib\Model\RconParameters.cs" />
<Compile Include="Lib\Serialization\IniFileEntryAttribute.cs" />
<Compile Include="Lib\Serialization\IniFiles.cs" />
<Compile Include="Lib\Serialization\IniSections.cs" />
<Compile Include="Lib\Serialization\SystemIniFile.cs" />
<Compile Include="Lib\ServerPlayers.cs" />
<Compile Include="Lib\ServerRcon.cs" />
<Compile Include="Lib\ViewConverters\EnumDescriptionTypeConverter.cs" />
<Compile Include="Lib\ViewConverters\MapNameValueConverter.cs" />
<Compile Include="Lib\ViewModel\PlayerInfo.cs" />
<Compile Include="Windows\AddUserWindow.xaml.cs">
<DependentUpon>AddUserWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\AutoUpdateWindow.xaml.cs">
<DependentUpon>AutoUpdateWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\CommandLineWindow.xaml.cs">
<DependentUpon>CommandLineWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\GameDataWindow.xaml.cs">
<DependentUpon>GameDataWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\GlobalSettingsControl.xaml.cs">
<DependentUpon>GlobalSettingsControl.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ModDetailsWindow.xaml.cs">
<DependentUpon>ModDetailsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\PlayerListWindow.xaml.cs">
<DependentUpon>PlayerListWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\PlayerProfileWindow.xaml.cs">
<DependentUpon>PlayerProfileWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\PluginsWindow.xaml.cs">
<DependentUpon>PluginsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ProcessorAffinityWindow.xaml.cs">
<DependentUpon>ProcessorAffinityWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ProfileSyncWindow.xaml.cs">
<DependentUpon>ProfileSyncWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ProgressWindow.xaml.cs">
<DependentUpon>ProgressWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\RconWindow.xaml.cs">
<DependentUpon>RconWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ServerMonitorWindow.xaml.cs">
<DependentUpon>ServerMonitorWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ServerSettingsControl.xaml.cs">
<DependentUpon>ServerSettingsControl.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\SettingsWindow.xaml.cs">
<DependentUpon>SettingsWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ShutdownWindow.xaml.cs">
<DependentUpon>ShutdownWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\GuildProfileWindow.xaml.cs">
<DependentUpon>GuildProfileWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\VersionFeedWindow.xaml.cs">
<DependentUpon>VersionFeedWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\WorkshopFilesWindow.xaml.cs">
<DependentUpon>WorkshopFilesWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\WorldSaveRestoreWindow.xaml.cs">
<DependentUpon>WorldSaveRestoreWindow.xaml</DependentUpon>
</Compile>
<Resource Include="Art\StatusOff.ico" />
<Resource Include="Art\StatusOn.ico" />
<Resource Include="Art\StatusStarting.ico" />
<Resource Include="Art\StatusUnknown.ico" />
<Resource Include="Art\Document.ico" />
<Resource Include="Art\Servers.ico" />
<Resource Include="Art\Shortcut.ico" />
<Content Include="..\packages\System.Data.SQLite.Core.1.0.112.1\build\net40\x64\SQLite.Interop.dll">
<Link>x64\SQLite.Interop.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="..\packages\System.Data.SQLite.Core.1.0.112.1\build\net40\x86\SQLite.Interop.dll">
<Link>x86\SQLite.Interop.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Resource Include="Art\Filter.ico" />
<Resource Include="Art\DropArrow.ico" />
<Content Include="Globalization\en-US\en-US.xaml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Config.Designer.cs">
<DependentUpon>Config.settings</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="Enums\LogEventType.cs" />
<Compile Include="Enums\PlayerFilterType.cs" />
<Compile Include="Enums\PlayerSortType.cs" />
<Compile Include="Enums\ServerProcessStatus.cs" />
<Compile Include="Enums\ServerProcessType.cs" />
<Compile Include="Enums\ServerProfileCategory.cs" />
<Compile Include="Enums\ServerStatus.cs" />
<Compile Include="Enums\AvailabilityStatus.cs" />
<Compile Include="Enums\WatcherServerStatus.cs" />
<Compile Include="ErrorHandling.cs" />
<None Include="Installer.iss" />
<Content Include="Styles\Default.xaml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Globalization\fr-FR\fr-FR.xaml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Globalization\pt-BR\pt-BR.xaml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Globalization\en-AU\en-AU.xaml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Page Include="Windows\AddUserWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\AutoUpdateWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\CommandLineWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\GameDataWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\GlobalSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ModDetailsWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\PlayerListWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\PlayerProfileWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\PluginsWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ProcessorAffinityWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ProfileSyncWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ProgressWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\RconWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ServerMonitorWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ServerSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\SettingsWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ShutdownWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\GuildProfileWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\VersionFeedWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\WorkshopFilesWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\WorldSaveRestoreWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Lib\BranchSnapshot.cs" />
<Compile Include="Lib\Events\ProfileEventArgs.cs" />
<Compile Include="Lib\GameData.cs" />
<Compile Include="Lib\Model\ModDetail.cs" />
<Compile Include="Lib\Model\ModDetailExtended.cs" />
<Compile Include="Lib\Model\ModDetailList.cs" />
<Compile Include="Lib\ServerProfileSnapshot.cs" />
<Compile Include="Lib\Server.cs" />
<Compile Include="Lib\ServerApp.cs" />
<Compile Include="Lib\ServerManager.cs" />
<Compile Include="Lib\ServerProfile.cs" />
<Compile Include="Lib\ServerRuntime.cs" />
<Compile Include="Lib\ServerStatusUpdate.cs" />
<Compile Include="Lib\ServerStatusUpdateRegistration.cs" />
<Compile Include="Lib\ServerStatusWatcher.cs" />
<Compile Include="Utils\ModUtils.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Content Include="GameData\Conan.gamedata">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="VersionFeed.xml" />
<None Include="VersionFeedBeta.xml" />
<None Include="Config.settings">
<Generator>PublicSettingsSingleFileGenerator</Generator>
<LastGenOutput>Config.Designer.cs</LastGenOutput>
</None>
<Content Include="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="DeployBetaToGithub.cmd" />
<None Include="DeployToGithub.cmd" />
<None Include="MakeLatestBetaVersionGithub.ps1" />
<None Include="MakeLatestVersionGithub.ps1" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<COMReference Include="IWshRuntimeLibrary">
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.6.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Add.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Cancel.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\ChangeNotes.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Checked.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Command.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Copy.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Delete.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Donate.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Down.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Download.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Drag.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Edit.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\favicon.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Find.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\FolderDelete.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\FolderExport.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\FolderImport.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\FolderOpen.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Help.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Logs.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\NoAvatar.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Paste.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Players.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Plugin.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Refresh.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Reload.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Save.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Settings.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Start.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Steam.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Stop.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Unchecked.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Up.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Validate.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Website.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Art\Zip.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ConanData\ConanData.csproj">
<Project>{96832688-29bd-464f-9dce-482e37bfc751}</Project>
<Name>ConanData</Name>
</ProjectReference>
<ProjectReference Include="..\ConanServerManager.Common\ConanServerManager.Common.csproj">
<Project>{630422ca-4bcc-4d1d-9701-87d8eaf0b209}</Project>
<Name>ConanServerManager.Common</Name>
</ProjectReference>
<ProjectReference Include="..\NeXtVdf\NeXt.Vdf.csproj">
<Project>{e4eda8b8-006c-4d41-822f-f64b6db0021f}</Project>
<Name>NeXt.Vdf</Name>
</ProjectReference>
<ProjectReference Include="..\Plugin.Common\Plugin.Common.csproj">
<Project>{679fe859-9a82-4ffb-a758-c1e8df915f58}</Project>
<Name>Plugin.Common</Name>
</ProjectReference>
<ProjectReference Include="..\QueryMaster\QueryMaster.csproj">
<Project>{4ca9c894-518f-42d7-bbe2-cfdfe7a03f8a}</Project>
<Name>QueryMaster</Name>
</ProjectReference>
<ProjectReference Include="..\ServerManager.Common\ServerManager.Common.csproj">
<Project>{7c99d9f7-0c65-4116-927a-94eb018c88fd}</Project>
<Name>ServerManager.Common</Name>
</ProjectReference>
<ProjectReference Include="..\ServerManager.Updater\ServerManager.Updater.csproj">
<Project>{3e0c9ee6-780f-4fef-ba03-e38062a5fdb6}</Project>
<Name>ServerManager.Updater</Name>
</ProjectReference>
<ProjectReference Include="..\WPFSharp.Globalizer\WPFSharp.Globalizer.csproj">
<Project>{715461e8-4e54-4993-80a8-8e72892135e0}</Project>
<Name>WPFSharp.Globalizer</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\System.Data.SQLite.Core.1.0.112.1\build\net40\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.112.1\build\net40\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.112.1\build\net40\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.112.1\build\net40\System.Data.SQLite.Core.targets'))" />
</Target>
</Project>

1900
src/ConanServerManager/Config.Designer.cs generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,531 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ServerManagerTool" GeneratedClassName="Config">
<Profiles />
<Settings>
<Setting Name="ConfigPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="SteamCmdInstallServerArgsFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)">+login {0} +force_install_dir "{1}" "+app_update {2} {3} {4}" +quit</Value>
</Setting>
<Setting Name="ServersInstallPath" Type="System.String" Scope="Application">
<Value Profile="(Default)">Servers</Value>
</Setting>
<Setting Name="DefaultServerProfileName" Type="System.String" Scope="Application">
<Value Profile="(Default)">Unnamed Profile</Value>
</Setting>
<Setting Name="DefaultServerName" Type="System.String" Scope="Application">
<Value Profile="(Default)">Unnamed Server</Value>
</Setting>
<Setting Name="ServerBinaryRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanSandbox\Binaries\Win64</Value>
</Setting>
<Setting Name="ServerConfigRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanSandbox\Saved\Config\WindowsServer</Value>
</Setting>
<Setting Name="ServerExeFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanSandboxServer-Win64-Test.exe</Value>
</Setting>
<Setting Name="ProfilesRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">profiles</Value>
</Setting>
<Setting Name="ProfileExtension" Type="System.String" Scope="Application">
<Value Profile="(Default)">.profile</Value>
</Setting>
<Setting Name="DataPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ServerProcessName" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanSandboxServer-Win64-Test</Value>
</Setting>
<Setting Name="LoadProfileExtensionList" Type="System.String" Scope="Application">
<Value Profile="(Default)">*.profile,*.ini</Value>
</Setting>
<Setting Name="MachinePublicIP" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ManageFirewallAutomatically" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="LogsRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">Logs</Value>
</Setting>
<Setting Name="LatestServerManagerVersionUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/latest.txt</Value>
</Setting>
<Setting Name="UpdateCheckTime" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">1440</Value>
</Setting>
<Setting Name="SavedFilesRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanSandbox\Saved</Value>
</Setting>
<Setting Name="UpgradeConfig" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="HelpUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://servermanagers.freeforums.net/thread/43/faq-frequently-asked</Value>
</Setting>
<Setting Name="CultureName" Type="System.String" Scope="User">
<Value Profile="(Default)">en-US</Value>
</Setting>
<Setting Name="SectionAdministrationIsExpanded" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="SectionAutomaticManagementIsExpanded" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AppPatchNotesUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://forums.funcom.com/c/conan-exiles/Patch-Notes</Value>
</Setting>
<Setting Name="AppUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://store.steampowered.com/app/440900/</Value>
</Setting>
<Setting Name="RunAsAdministratorPrompt" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoUpdate_EnableUpdate" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AutoUpdate_UpdatePeriod" Type="System.Int32" Scope="User">
<Value Profile="(Default)">60</Value>
</Setting>
<Setting Name="ServerShutdown_GracePeriod" Type="System.Int32" Scope="User">
<Value Profile="(Default)">15</Value>
</Setting>
<Setting Name="ServerShutdown_GraceMessage1" Type="System.String" Scope="User">
<Value Profile="(Default)">Server shutdown required. Server will shutdown in {minutes} minutes. Please logout before shutdown to prevent character corruption.</Value>
</Setting>
<Setting Name="ServerShutdown_GraceMessage2" Type="System.String" Scope="User">
<Value Profile="(Default)">Server shutdown required. Server will shutdown in 1 minute. Please logout before shutdown to prevent character corruption.</Value>
</Setting>
<Setting Name="ServerShutdown_GraceMessage3" Type="System.String" Scope="User">
<Value Profile="(Default)">Server shutdown required. Server is shutting down now.</Value>
</Setting>
<Setting Name="LastUpdatedTimeFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">LastUpdatedSM.txt</Value>
</Setting>
<Setting Name="ServerShutdown_WorldSaveMessage" Type="System.String" Scope="User">
<Value Profile="(Default)">Server shutdown required. Server is about to shutdown, performing a world save.</Value>
</Setting>
<Setting Name="ServerShutdown_EnableWorldSave" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="LauncherFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">RunServer.cmd</Value>
</Setting>
<Setting Name="SteamCmdInstallModArgsFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)">+login {0} +workshop_download_item {1} {2} +quit</Value>
</Setting>
<Setting Name="AppSteamWorkshopFolderRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">steamapps\workshop\content\440900\</Value>
</Setting>
<Setting Name="ServerModsRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanSandbox\Mods</Value>
</Setting>
<Setting Name="AutoUpdate_CacheDir" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ServerUpdate_UpdateModsWhenUpdatingServer" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerUpdate_ForceUpdateMods" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ServerUpdate_ForceCopyMods" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="SavedRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">Saved</Value>
</Setting>
<Setting Name="SteamCmdRedirectOutput" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AutoUpdate_UseSmartCopy" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="StyleName" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="WorkshopCacheFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">workshopcache_440900.json</Value>
</Setting>
<Setting Name="Email_Host" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="Email_Port" Type="System.Int32" Scope="User">
<Value Profile="(Default)">25</Value>
</Setting>
<Setting Name="Email_UseSSL" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="Email_UseDetaultCredentials" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="Email_Username" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="Email_Password" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="Email_From" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="Email_To" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="EmailNotify_AutoUpdate" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="EmailNotify_AutoRestart" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="SteamCmd_Username" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="SteamCmd_AnonymousUsername" Type="System.String" Scope="Application">
<Value Profile="(Default)">anonymous</Value>
</Setting>
<Setting Name="SteamCmd_UseAnonymousCredentials" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="SteamWorkshopFolderRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">steamapps\workshop</Value>
</Setting>
<Setting Name="AppSteamWorkshopFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">appworkshop_440900.acf</Value>
</Setting>
<Setting Name="SteamCmd_Password" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ValidateProfileOnServerStart" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerUpdate_OnServerStart" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AppId" Type="System.String" Scope="Application">
<Value Profile="(Default)">440900</Value>
</Setting>
<Setting Name="AppIdServer" Type="System.String" Scope="Application">
<Value Profile="(Default)">443030</Value>
</Setting>
<Setting Name="ServerAppIdFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">steam_appid.txt</Value>
</Setting>
<Setting Name="LatestServerManagerBetaVersionUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/beta/latest.txt</Value>
</Setting>
<Setting Name="LatestServerManagerBetaDownloadUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/beta/latest.zip</Value>
</Setting>
<Setting Name="LatestServerManagerDownloadUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/latest.zip</Value>
</Setting>
<Setting Name="LatestServerManagerPatchNotesUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://servermanagers.freeforums.net/thread/36/downloads</Value>
</Setting>
<Setting Name="LatestServerManagerBetaPatchNotesUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://servermanagers.freeforums.net/board/44/beta-testers</Value>
</Setting>
<Setting Name="ServerBlacklistFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">blacklist.txt</Value>
</Setting>
<Setting Name="SectionServerFilesIsExpanded" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="EmailNotify_ShutdownRestart" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="WorkshopCache_ExpiredHours" Type="System.Int32" Scope="User">
<Value Profile="(Default)">168</Value>
</Setting>
<Setting Name="DefaultServerRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">server</Value>
</Setting>
<Setting Name="ServerStatusUrlFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)" />
</Setting>
<Setting Name="BackupRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">_backup_</Value>
</Setting>
<Setting Name="ServerGameConfigFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">Game.ini</Value>
</Setting>
<Setting Name="ServerShutdown_CancelMessage" Type="System.String" Scope="User">
<Value Profile="(Default)">Server shutdown has been cancelled.</Value>
</Setting>
<Setting Name="ServerUpdate_ForceUpdateModsIfNoSteamInfo" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoBackup_EnableBackup" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AutoBackup_BackupPeriod" Type="System.Int32" Scope="User">
<Value Profile="(Default)">60</Value>
</Setting>
<Setting Name="EmailNotify_AutoBackup" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AutoBackup_DeleteOldFiles" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoBackup_DeleteInterval" Type="System.Int32" Scope="User">
<Value Profile="(Default)">30</Value>
</Setting>
<Setting Name="ServerBackup_WorldSaveMessage" Type="System.String" Scope="User">
<Value Profile="(Default)">A world save is about to be performed, you may experience some lag during this process. Please be patient.</Value>
</Setting>
<Setting Name="AutoUpdate_RetryOnFail" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="SteamWebAPIKeyHelpUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://servermanagers.freeforums.net/thread/40/get-own-steam-web-api</Value>
</Setting>
<Setting Name="AutoUpdate_ShowUpdateReason" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="BackupServerExtension" Type="System.String" Scope="Application">
<Value Profile="(Default)">.smbak</Value>
</Setting>
<Setting Name="ScheduledTasksCheckTime" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">1</Value>
</Setting>
<Setting Name="AutoUpdate_ValidateServerFiles" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoUpdate_OverrideServerStartup" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ServerManagerUniqueKey" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="AutoUpdate_ParallelUpdate" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoUpdate_UpdateReasonPrefix" Type="System.String" Scope="User">
<Value Profile="(Default)">Server Update Reason:</Value>
</Setting>
<Setting Name="Alert_ServerStopMessage" Type="System.String" Scope="User">
<Value Profile="(Default)">The server is stopping.</Value>
</Setting>
<Setting Name="Alert_ServerShutdownMessage" Type="System.String" Scope="User">
<Value Profile="(Default)">The server is shutting down.</Value>
</Setting>
<Setting Name="Alert_ServerStartedMessage" Type="System.String" Scope="User">
<Value Profile="(Default)">The server is starting.</Value>
</Setting>
<Setting Name="Alert_BackupProcessError" Type="System.String" Scope="User">
<Value Profile="(Default)">The server backup process was performed but an error occurred.</Value>
</Setting>
<Setting Name="Alert_ShutdownProcessError" Type="System.String" Scope="User">
<Value Profile="(Default)">The server shutdown process was performed but an error occurred.</Value>
</Setting>
<Setting Name="Alert_RestartProcessError" Type="System.String" Scope="User">
<Value Profile="(Default)">The server restart process was performed but an error occurred.</Value>
</Setting>
<Setting Name="Alert_UpdateProcessError" Type="System.String" Scope="User">
<Value Profile="(Default)">The server update process was performed but an error occurred.</Value>
</Setting>
<Setting Name="Alert_ModUpdateDetected" Type="System.String" Scope="User">
<Value Profile="(Default)">Mod updates have been detected:</Value>
</Setting>
<Setting Name="ServerManagerPluginUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://servermanagers.freeforums.net/board/36/plugins</Value>
</Setting>
<Setting Name="ServerManagerForumUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://servermanagers.freeforums.net/</Value>
</Setting>
<Setting Name="DefaultGlobalizationFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">Globalization\en-US\en-US.xaml</Value>
</Setting>
<Setting Name="ScheduledTaskFolder" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanServerManager</Value>
</Setting>
<Setting Name="FirewallRulePrefix" Type="System.String" Scope="Application">
<Value Profile="(Default)">ConanServer:</Value>
</Setting>
<Setting Name="DonationUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=bletch1971%40hotmail%2ecom&amp;lc=US&amp;item_name=Server%20Manager&amp;currency_code=USD&amp;bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted</Value>
</Setting>
<Setting Name="ServerManagerCode" Type="System.String" Scope="Application">
<Value Profile="(Default)">F2653C3D-BC83-440A-AD99-FD9D9466DE04</Value>
</Setting>
<Setting Name="BackupExtension" Type="System.String" Scope="Application">
<Value Profile="(Default)">.zip</Value>
</Setting>
<Setting Name="UpdaterPrefix" Type="System.String" Scope="Application">
<Value Profile="(Default)">Conan_</Value>
</Setting>
<Setting Name="UpdaterFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">ServerManagerUpdater.exe</Value>
</Setting>
<Setting Name="ServerShutdown_WorldSaveDelay" Type="System.Int32" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="ServerShutdown_UseShutdownCommand" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="Alert_ServerStatusChange" Type="System.String" Scope="User">
<Value Profile="(Default)">Server Status:</Value>
</Setting>
<Setting Name="Alert_ServerUpdate" Type="System.String" Scope="User">
<Value Profile="(Default)">Game Server Update</Value>
</Setting>
<Setting Name="Alert_UpdateResults" Type="System.String" Scope="User">
<Value Profile="(Default)">Update performed, includes:</Value>
</Setting>
<Setting Name="ServerCommandLineArgsIPMatchFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)">-MultiHome={0}</Value>
</Setting>
<Setting Name="ServerCommandLineArgsPortMatchFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)">-QueryPort={0}</Value>
</Setting>
<Setting Name="BackupPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ServerShutdown_AllMessagesShowReason" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="GameDataRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">GameData</Value>
</Setting>
<Setting Name="GameDataExtension" Type="System.String" Scope="Application">
<Value Profile="(Default)">.gamedata</Value>
</Setting>
<Setting Name="GameDataApplication" Type="System.String" Scope="Application">
<Value Profile="(Default)">conan</Value>
</Setting>
<Setting Name="CloseShutdownWindowWhenFinished" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerManagerVersionFeedUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/VersionFeed.xml</Value>
</Setting>
<Setting Name="ServerManagerVersionBetaFeedUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://raw.githubusercontent.com/Bletch1971/ServerManagers/master/CSM/beta/VersionFeed.xml</Value>
</Setting>
<Setting Name="DefaultServerBranchName" Type="System.String" Scope="Application">
<Value Profile="(Default)">live</Value>
</Setting>
<Setting Name="SteamCmdInstallServerBetaNameArgsFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)">-beta {0}</Value>
</Setting>
<Setting Name="SteamCmdInstallServerBetaPasswordArgsFormat" Type="System.String" Scope="Application">
<Value Profile="(Default)">-betapassword {0}</Value>
</Setting>
<Setting Name="ServerBranchFolderPrefix" Type="System.String" Scope="Application">
<Value Profile="(Default)">__</Value>
</Setting>
<Setting Name="AppSteamManifestFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">appmanifest_443030.acf</Value>
</Setting>
<Setting Name="SteamManifestFolderRelativePath" Type="System.String" Scope="Application">
<Value Profile="(Default)">steamapps</Value>
</Setting>
<Setting Name="ServerEngineConfigFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">Engine.ini</Value>
</Setting>
<Setting Name="ServerSettingsConfigFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">ServerSettings.ini</Value>
</Setting>
<Setting Name="ServerModListFile" Type="System.String" Scope="User">
<Value Profile="(Default)">modlist.txt</Value>
</Setting>
<Setting Name="RCON_PlayerListFilter" Type="System.Int32" Scope="User">
<Value Profile="(Default)">19</Value>
</Setting>
<Setting Name="RCON_PlayerListSort" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1</Value>
</Setting>
<Setting Name="SendMessageDelay" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">5000</Value>
</Setting>
<Setting Name="PlayerImageFileExtension" Type="System.String" Scope="Application">
<Value Profile="(Default)">.png</Value>
</Setting>
<Setting Name="PlayerListFilter" Type="System.Int32" Scope="User">
<Value Profile="(Default)">19</Value>
</Setting>
<Setting Name="PlayerListSort" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1</Value>
</Setting>
<Setting Name="ServerShutdownCommand" Type="System.String" Scope="Application">
<Value Profile="(Default)">doexit</Value>
</Setting>
<Setting Name="ServerSaveCommand" Type="System.String" Scope="Application">
<Value Profile="(Default)">saveworld</Value>
</Setting>
<Setting Name="ServerStatus_EnableActions" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerStatus_ShowActionConfirmation" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="DefaultStyleFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">Styles\Default.xaml</Value>
</Setting>
<Setting Name="ServerWhitelistFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">whitelist.txt</Value>
</Setting>
<Setting Name="ServerCommandLineStandardArgs" Type="System.String" Scope="Application">
<Value Profile="(Default)">-listen -nosteamclient -game -server -log</Value>
</Setting>
<Setting Name="AutoUpdate_SequencialDelayPeriod" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="BackupWorldFile" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="GameDataUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://servermanagers.freeforums.net/</Value>
</Setting>
<Setting Name="MainWindow_Width" Type="System.Double" Scope="User">
<Value Profile="(Default)">1100</Value>
</Setting>
<Setting Name="MainWindow_Height" Type="System.Double" Scope="User">
<Value Profile="(Default)">900</Value>
</Setting>
<Setting Name="ServerMonitorWindow_Width" Type="System.Double" Scope="User">
<Value Profile="(Default)">900</Value>
</Setting>
<Setting Name="ServerMonitorWindow_Height" Type="System.Double" Scope="User">
<Value Profile="(Default)">500</Value>
</Setting>
<Setting Name="UpdateDirectoryPermissions" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoUpdate_VerifyServerAfterUpdate" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ServerShutdown_CheckForOnlinePlayers" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerShutdown_SendShutdownMessages" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AutoRestart_EnabledGracePeriod" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="AutoRestart_GracePeriod" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="ServerStatusWatcher_LocalStatusQueryDelay" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">15000</Value>
</Setting>
<Setting Name="ServerStatusWatcher_RemoteStatusQueryDelay" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">120000</Value>
</Setting>
<Setting Name="MainWindow_MinimizeToTray" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ServerMonitorMessageOutput_Height" Type="System.Double" Scope="User">
<Value Profile="(Default)">100</Value>
</Setting>
<Setting Name="Alert_ServerStartedMessageIncludeIPandPort" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ManagePublicIPAutomatically" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
</Settings>
</SettingsFile>

View file

@ -0,0 +1,11 @@
namespace ServerManagerTool.Enums
{
public enum AvailabilityStatus
{
Unknown,
NeedPublicIP,
Unavailable,
WaitingForPublication,
Available
}
}

View file

@ -0,0 +1,9 @@
namespace ServerManagerTool.Enums
{
public enum LogEventType
{
All,
Chat,
Event
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace ServerManagerTool.Enums
{
[Flags]
public enum PlayerFilterType
{
None = 0,
Offline = 0x1,
Online = 0x2,
Admin = 0x4,
Invalid = 0x10,
Whitelisted = 0x20,
}
}

View file

@ -0,0 +1,10 @@
namespace ServerManagerTool.Enums
{
public enum PlayerSortType
{
Online = 0,
Name = 1,
Tribe = 2,
LastOnline = 3,
}
}

View file

@ -0,0 +1,25 @@
namespace ServerManagerTool.Enums
{
public enum ServerProcessStatus
{
/// <summary>
/// The server binary could not be found
/// </summary>
NotInstalled,
/// <summary>
/// The server binary was found, but the process was not.
/// </summary>
Stopped,
/// <summary>
/// The server binary was found, the process was found, but no permissions to access the process.
/// </summary>
Unknown,
/// <summary>
/// The server process was found
/// </summary>
Running,
}
}

View file

@ -0,0 +1,14 @@
namespace ServerManagerTool.Enums
{
public enum ServerProcessType
{
Unknown = 0,
AutoBackup,
AutoUpdate,
AutoShutdown1,
AutoShutdown2,
Backup,
Shutdown,
Restart,
}
}

View file

@ -0,0 +1,9 @@
namespace ServerManagerTool.Enums
{
public enum ServerProfileCategory
{
Administration,
AutomaticManagement,
ServerFiles,
}
}

View file

@ -0,0 +1,13 @@
namespace ServerManagerTool.Enums
{
public enum ServerStatus
{
Unknown,
Stopping,
Stopped,
Initializing,
Running,
Updating,
Uninstalled
}
}

View file

@ -0,0 +1,40 @@
namespace ServerManagerTool.Enums
{
public enum WatcherServerStatus
{
/// <summary>
/// The server binary couldnot be found.
/// </summary>
NotInstalled,
/// <summary>
/// The server binary was found, but the process was not
/// </summary>
Stopped,
/// <summary>
/// The server binary was found, the process was found, but no permissions to access the process.
/// </summary>
Unknown,
/// <summary>
/// The server process was found, but the server is not responding on its port
/// </summary>
Initializing,
/// <summary>
/// The server is responding locally on its port, a local check was made
/// </summary>
RunningLocalCheck,
/// <summary>
/// The server is responding locally on its port, a public check was made
/// </summary>
RunningExternalCheck,
/// <summary>
/// The server is responding externally on its port
/// </summary>
Published,
}
}

View file

@ -0,0 +1,117 @@
using ServerManagerTool.Common.Utils;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace ServerManagerTool
{
public static class ErrorHandling
{
public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// Oops! Bad news everyone - the app is going down!
// Write out a log file with all the details so users can send us the info...
var file = Path.GetTempFileName();
var crashFile = file + ".dmp";
try
{
MiniDumpToFile(crashFile);
}
catch { }
try
{
var details = new StringBuilder();
details.AppendLine("Server Manager Crash Report");
details.AppendLine($"Please report this crash to the Server Manager forums - {Config.Default.ServerManagerForumUrl}");
details.AppendLine();
details.AppendLine($"Assembly: {Assembly.GetEntryAssembly()}");
details.AppendLine($"Version: {App.Instance.Version}");
details.AppendLine($"Code: {Config.Default.ServerManagerCode}");
details.AppendLine($"IsAdministrator: {SecurityUtils.IsAdministrator()}");
details.AppendLine();
details.AppendLine($"Windows Platform: {Environment.OSVersion.Platform}");
details.AppendLine($"Windows Version: {Environment.OSVersion.VersionString}");
details.AppendLine();
details.AppendLine($"Crash Dump: {crashFile}");
details.AppendLine();
var exception = e.ExceptionObject as Exception;
if (exception != null)
{
details.AppendLine("Exception Message:");
details.AppendLine(exception.Message);
details.AppendLine();
details.AppendLine("Stack Trace:");
details.AppendLine(exception.StackTrace);
}
File.WriteAllText(file, details.ToString());
var message = new StringBuilder();
message.AppendLine("OOPS! The Server Manager has suffered from an internal error and must shut down, this is probably a bug and should be reported. The error files are:");
message.AppendLine($"Error File: {file}");
message.AppendLine($"Crash Dump: {crashFile}");
details.AppendLine();
details.AppendLine();
message.AppendLine($"Please report this crash to the Server Manager forums - {Config.Default.ServerManagerForumUrl}");
message.AppendLine("The crash log will now be opened in notepad.");
var result = MessageBox.Show(message.ToString(), "Server Manager crashed", MessageBoxButton.OK, MessageBoxImage.Exclamation);
if (result == MessageBoxResult.OK)
{
Process.Start("notepad.exe", file);
}
}
catch (Exception ex)
{
try
{
File.WriteAllText(file, $"Exception trying to write exception: {ex.Message}\r\nStacktrace: {ex.StackTrace}");
}
catch { }
}
}
internal enum MinidumpType
{
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
MiniDumpWithFullMemory = 0x00000002,
MiniDumpWithHandleData = 0x00000004,
MiniDumpFilterMemory = 0x00000008,
MiniDumpScanMemory = 0x00000010,
MiniDumpWithUnloadedModules = 0x00000020,
MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
MiniDumpFilterModulePaths = 0x00000080,
MiniDumpWithProcessThreadData = 0x00000100,
MiniDumpWithPrivateReadWriteMemory = 0x00000200,
MiniDumpWithoutOptionalData = 0x00000400,
MiniDumpWithFullMemoryInfo = 0x00000800,
MiniDumpWithThreadInfo = 0x00001000,
MiniDumpWithCodeSegs = 0x00002000
}
[DllImport("dbghelp.dll")]
static extern bool MiniDumpWriteDump(IntPtr hProcess, Int32 ProcessId, IntPtr hFile, MinidumpType DumpType, IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallackParam);
public static void MiniDumpToFile(String fileToDump)
{
var fsToDump = File.Create(fileToDump);
Process thisProcess = Process.GetCurrentProcess();
MiniDumpWriteDump(thisProcess.Handle, thisProcess.Id, fsToDump.SafeFileHandle.DangerousGetHandle(), MinidumpType.MiniDumpWithFullMemory, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
fsToDump.Close();
}
}
}

View file

@ -0,0 +1,51 @@
{
"Application": "conan",
"Version": "1.1.0",
"Created": "2020-09-20T00:00:00.0000000Z",
"GameMaps": [
{
"ClassName": "ConanSandbox",
"Description": "The Exiled Lands",
"SaveFileName": "game.db",
"Mod": "Conan"
},
{
"ClassName": "/Game/DLC_EXT/DLC_Siptah/Maps/DLC_Isle_of_Siptah",
"Description": "Isle of Siptah",
"SaveFileName": "DLC_Siptah.db",
"Mod": "Conan"
}
],
"Branches": [
{
"BranchName": "testlive",
"Description": "TestLive"
}
],
"ServerRegions": [
{
"RegionNumber": "0",
"Description": "Europe"
},
{
"RegionNumber": "1",
"Description": "North America"
},
{
"RegionNumber": "2",
"Description": "Asia"
},
{
"RegionNumber": "3",
"Description": "Australia"
},
{
"RegionNumber": "4",
"Description": "South America"
},
{
"RegionNumber": "5",
"Description": "Japan"
}
]
}

View file

@ -0,0 +1,12 @@
<Globalization:GlobalizationResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Globalization="clr-namespace:WPFSharp.Globalizer;assembly=WPFSharp.Globalizer"
Name="en-AU"
LinkedStyle="en-AU-style"
>
<sys:String x:Key="Generic_TranslatedBy">Bletch</sys:String>
</Globalization:GlobalizationResourceDictionary>

File diff suppressed because it is too large Load diff

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,36 @@
using System;
using System.Collections.Generic;
namespace ServerManagerTool.Lib
{
public class BranchSnapshot
{
public string BranchName = string.Empty;
public string BranchPassword = string.Empty;
}
public class BranchSnapshotComparer : IEqualityComparer<BranchSnapshot>
{
public bool Equals(BranchSnapshot x, BranchSnapshot y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (x is null || y is null)
return false;
//Check whether the snapshot' properties are equal.
return x.BranchName == y.BranchName;
}
public int GetHashCode(BranchSnapshot snapshot)
{
//Check whether the object is null
if (snapshot is null) return 0;
//Get hash code for the Name field if it is not null.
return snapshot.BranchName == null ? 0 : snapshot.BranchName.GetHashCode();
}
}
}

View file

@ -0,0 +1,14 @@
using System;
namespace ServerManagerTool.Lib
{
public class ProfileEventArgs : EventArgs
{
public ProfileEventArgs(ServerProfile profile)
{
Profile = profile;
}
public ServerProfile Profile;
}
}

View file

@ -0,0 +1,98 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Lib
{
public static class GameData
{
public static string MainDataFolder = Path.Combine(Environment.CurrentDirectory, Config.Default.GameDataRelativePath);
public static string UserDataFolder = Path.Combine(Config.Default.DataPath, Config.Default.GameDataRelativePath);
private static MainGameData gameData = null;
public static void Initialize()
{
// read static game data
GameDataUtils.ReadAllData(out gameData, MainDataFolder, Config.Default.GameDataExtension, Config.Default.GameDataApplication);
// read user game data
MainGameData userGameData = new MainGameData();
if (!UserDataFolder.Equals(MainDataFolder, StringComparison.OrdinalIgnoreCase))
{
GameDataUtils.ReadAllData(out userGameData, UserDataFolder, Config.Default.GameDataExtension, Config.Default.GameDataApplication, true);
}
// game maps
gameData.GameMaps.AddRange(userGameData.GameMaps);
if (gameData.GameMaps.Count > 0)
{
var maps = gameMaps.ToList();
maps.AddRange(gameData.GameMaps.ConvertAll(item => new ComboBoxItem { ValueMember = item.ClassName, DisplayMember = item.Description }));
gameMaps = maps.ToArray();
}
// branches
gameData.Branches.AddRange(userGameData.Branches);
if (gameData.Branches.Count > 0)
{
var allBranches = branches.ToList();
allBranches.AddRange(gameData.Branches.ConvertAll(item => new ComboBoxItem { ValueMember = item.BranchName, DisplayMember = item.Description }));
branches = allBranches.ToArray();
}
// server regions
gameData.ServerRegions.AddRange(userGameData.ServerRegions);
if (gameData.ServerRegions.Count > 0)
{
var allServerRegions = serverRegions.ToList();
allServerRegions.AddRange(gameData.ServerRegions.ConvertAll(item => new ComboBoxItem { ValueMember = item.RegionNumber, DisplayMember = item.Description }));
serverRegions = allServerRegions.ToArray();
}
}
public static string FriendlyNameForClass(string className, bool returnNullIfNotFound = false) => string.IsNullOrWhiteSpace(className) ? (returnNullIfNotFound ? null : string.Empty) : GlobalizedApplication.Instance.GetResourceString(className) ?? (returnNullIfNotFound ? null : className);
#region Game Maps
private static ComboBoxItem[] gameMaps = new ComboBoxItem[]
{
new ComboBoxItem { ValueMember="", DisplayMember="" },
};
public static IEnumerable<ComboBoxItem> GetGameMaps() => gameMaps.Select(m => m.Duplicate());
public static string FriendlyMapNameForClass(string className, bool returnEmptyIfNotFound = false) => string.IsNullOrWhiteSpace(className) ? string.Empty : GlobalizedApplication.Instance.GetResourceString(className) ?? gameData?.GameMaps?.FirstOrDefault(i => i.ClassName.Equals(className))?.Description ?? (returnEmptyIfNotFound ? string.Empty : className);
public static string MapSaveNameForClass(string className, bool returnEmptyIfNotFound = false) => string.IsNullOrWhiteSpace(className) ? string.Empty : gameData?.GameMaps?.FirstOrDefault(i => i.ClassName.Equals(className))?.SaveFileName ?? (returnEmptyIfNotFound ? string.Empty : className);
#endregion
#region Branches
private static ComboBoxItem[] branches = new[]
{
new ComboBoxItem { ValueMember="", DisplayMember=FriendlyNameForClass(Config.Default.DefaultServerBranchName) },
};
public static IEnumerable<ComboBoxItem> GetBranches() => branches.Select(d => d.Duplicate());
public static string FriendlyBranchName(string branchName, bool returnEmptyIfNotFound = false) => string.IsNullOrWhiteSpace(branchName) ? string.Empty : GlobalizedApplication.Instance.GetResourceString(branchName) ?? gameData?.Branches?.FirstOrDefault(i => i.BranchName.Equals(branchName))?.Description ?? (returnEmptyIfNotFound ? string.Empty : branchName);
#endregion
#region Server Regions
private static ComboBoxItem[] serverRegions = new ComboBoxItem[0];
public static IEnumerable<ComboBoxItem> GetServerRegions() => serverRegions.Select(d => d.Duplicate());
public static string FriendlyServerRegionName(string regionNumber, bool returnEmptyIfNotFound = false) => string.IsNullOrWhiteSpace(regionNumber) ? string.Empty : GlobalizedApplication.Instance.GetResourceString($"ServerRegion_{regionNumber}") ?? gameData?.ServerRegions?.FirstOrDefault(i => i.RegionNumber.Equals(regionNumber))?.Description ?? (returnEmptyIfNotFound ? string.Empty : regionNumber);
#endregion
}
}

View file

@ -0,0 +1,242 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Utils;
using System;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Lib
{
public class ModDetail : DependencyObject
{
private readonly GlobalizedApplication _globalizer = GlobalizedApplication.Instance;
public static readonly DependencyProperty AppIdProperty = DependencyProperty.Register(nameof(AppId), typeof(string), typeof(ModDetail), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IndexProperty = DependencyProperty.Register(nameof(Index), typeof(int), typeof(ModDetail), new PropertyMetadata(0));
public static readonly DependencyProperty IsFirstProperty = DependencyProperty.Register(nameof(IsFirst), typeof(bool), typeof(ModDetail), new PropertyMetadata(false));
public static readonly DependencyProperty IsLastProperty = DependencyProperty.Register(nameof(IsLast), typeof(bool), typeof(ModDetail), new PropertyMetadata(false));
public static readonly DependencyProperty LastWriteTimeProperty = DependencyProperty.Register(nameof(LastWriteTime), typeof(DateTime), typeof(ModDetail), new PropertyMetadata(DateTime.MinValue));
public static readonly DependencyProperty LastTimeUpdatedProperty = DependencyProperty.Register(nameof(LastTimeUpdated), typeof(int), typeof(ModDetail), new PropertyMetadata(0));
public static readonly DependencyProperty ModIdProperty = DependencyProperty.Register(nameof(ModId), typeof(string), typeof(ModDetail), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty ModTypeProperty = DependencyProperty.Register(nameof(ModType), typeof(string), typeof(ModDetail), new PropertyMetadata(ModUtils.MODTYPE_UNKNOWN));
public static readonly DependencyProperty ModTypeStringProperty = DependencyProperty.Register(nameof(ModTypeString), typeof(string), typeof(ModDetail), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty ModUrlProperty = DependencyProperty.Register(nameof(ModUrl), typeof(string), typeof(ModDetail), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty TimeUpdatedProperty = DependencyProperty.Register(nameof(TimeUpdated), typeof(int), typeof(ModDetail), new PropertyMetadata(0));
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(ModDetail), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsValidProperty = DependencyProperty.Register(nameof(IsValid), typeof(bool), typeof(ModDetail), new PropertyMetadata(false));
public string AppId
{
get { return (string)GetValue(AppIdProperty); }
set { SetValue(AppIdProperty, value); }
}
public int Index
{
get { return (int)GetValue(IndexProperty); }
set { SetValue(IndexProperty, value); }
}
public bool IsFirst
{
get { return (bool)GetValue(IsFirstProperty); }
set { SetValue(IsFirstProperty, value); }
}
public bool IsLast
{
get { return (bool)GetValue(IsLastProperty); }
set { SetValue(IsLastProperty, value); }
}
public DateTime LastWriteTime
{
get { return (DateTime)GetValue(LastWriteTimeProperty); }
set { SetValue(LastWriteTimeProperty, value); }
}
public int LastTimeUpdated
{
get { return (int)GetValue(LastTimeUpdatedProperty); }
set { SetValue(LastTimeUpdatedProperty, value); }
}
public string ModId
{
get { return (string)GetValue(ModIdProperty); }
set { SetValue(ModIdProperty, value); }
}
public string ModType
{
get { return (string)GetValue(ModTypeProperty); }
set
{
SetValue(ModTypeProperty, value);
SetModTypeString();
}
}
public string ModTypeString
{
get { return (string)GetValue(ModTypeStringProperty); }
set { SetValue(ModTypeStringProperty, value); }
}
public int TimeUpdated
{
get { return (int)GetValue(TimeUpdatedProperty); }
set { SetValue(TimeUpdatedProperty, value); }
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set
{
SetValue(TitleProperty, value);
TitleFilterString = value?.ToLower();
}
}
public bool IsValid
{
get { return (bool)GetValue(IsValidProperty); }
set { SetValue(IsValidProperty, value); }
}
public bool IsValidModType => !string.IsNullOrWhiteSpace(ModType) && (ModType.Equals(ModUtils.MODTYPE_MAP) || ModType.Equals(ModUtils.MODTYPE_MOD));
public string LastWriteTimeString => LastWriteTime == DateTime.MinValue ? string.Empty : LastWriteTime.ToString();
public string LastWriteTimeSortString => LastWriteTime == DateTime.MinValue ? string.Empty : LastWriteTime.ToString("yyyyMMdd_HHmmss");
public string MapName { get; set; }
public string ModUrl => $"http://steamcommunity.com/sharedfiles/filedetails/?id={ModId}";
public string TimeUpdatedString => TimeUpdated <= 0 ? string.Empty : DateTimeUtils.UnixTimeStampToDateTime(TimeUpdated).ToString();
public string TimeUpdatedSortString => TimeUpdated <= 0 ? string.Empty : DateTimeUtils.UnixTimeStampToDateTime(TimeUpdated).ToString("yyyyMMdd_HHmmss");
public string TitleFilterString
{
get;
private set;
}
public bool UpToDate => !IsValid && TimeUpdated == -1 || LastTimeUpdated > 0 && LastTimeUpdated == TimeUpdated;
public long FolderSize { get; set; }
public string FolderSizeString
{
get
{
// GB
var divisor = Math.Pow(1024, 3);
if (FolderSize > divisor)
return $"{FolderSize / divisor:N2} GB";
// MB
divisor = Math.Pow(1024, 2);
if (FolderSize > divisor)
return $"{FolderSize / divisor:N2} MB";
// KB
divisor = Math.Pow(1024, 1);
if (FolderSize > divisor)
return $"{FolderSize / divisor:N2} KB";
return $"{FolderSize} B";
}
}
public void PopulateExtended(string modsRootFolder)
{
var modExtended = new ModDetailExtended(ModId);
modExtended.PopulateExtended(modsRootFolder);
PopulateExtended(modExtended);
}
public void PopulateExtended(ModDetailExtended extended)
{
LastTimeUpdated = extended.LastTimeUpdated;
LastWriteTime = extended.LastWriteTime;
MapName = extended.MapName;
ModType = extended.ModType;
FolderSize = extended.FolderSize;
}
public void SetModTypeString()
{
if (string.IsNullOrWhiteSpace(ModType))
ModTypeString = _globalizer.GetResourceString("ModType_Unknown");
switch (ModType)
{
case ModUtils.MODTYPE_MAP:
ModTypeString = _globalizer.GetResourceString("ModType_Map");
break;
case ModUtils.MODTYPE_MOD:
ModTypeString = _globalizer.GetResourceString("ModType_Mod");
break;
default:
if (string.IsNullOrWhiteSpace(AppId))
ModTypeString = _globalizer.GetResourceString("ModType_Unknown");
else
ModTypeString = _globalizer.GetResourceString("ModType_NotDownloaded");
break;
}
}
public static ModDetail GetModDetail(PublishedFileDetail detail)
{
var result = new ModDetail()
{
AppId = detail.creator_app_id,
ModId = detail.publishedfileid,
TimeUpdated = detail.time_updated,
Title = detail.title,
IsValid = true,
};
return result;
}
public static ModDetail GetModDetail(WorkshopFileDetail detail)
{
var result = new ModDetail()
{
AppId = detail.creator_appid,
ModId = detail.publishedfileid,
TimeUpdated = detail.time_updated,
Title = detail.title,
IsValid = true,
};
return result;
}
public static ModDetail GetModDetail(WorkshopFileItem detail)
{
var result = new ModDetail()
{
AppId = detail.AppId,
ModId = detail.WorkshopId,
TimeUpdated = detail.TimeUpdated,
Title = detail.Title,
IsValid = true,
};
return result;
}
public override string ToString()
{
return $"{ModId} - {Title}";
}
}
}

View file

@ -0,0 +1,61 @@
using ServerManagerTool.Utils;
using System;
using System.IO;
namespace ServerManagerTool.Lib
{
public class ModDetailExtended
{
public ModDetailExtended(string modId)
{
ModId = modId;
}
public string MapName { get; set; }
private string ModId { get; set; }
public string ModType { get; set; }
public DateTime LastWriteTime { get; set; }
public int LastTimeUpdated { get; set; }
public long FolderSize { get; set; }
public void PopulateExtended(string modsRootFolder)
{
try
{
FolderSize = 0;
LastWriteTime = DateTime.MinValue;
ModType = ModUtils.MODTYPE_UNKNOWN;
MapName = string.Empty;
if (string.IsNullOrWhiteSpace(modsRootFolder) || !Directory.Exists(modsRootFolder))
return;
var modFileName = $"{ModId}.pak";
var modFile = Path.Combine(modsRootFolder, modFileName);
if (!string.IsNullOrWhiteSpace(modFile) && File.Exists(modFile))
{
var file = new FileInfo(modFile);
LastWriteTime = file.LastWriteTime;
FolderSize += file.Length;
ModType = ModUtils.MODTYPE_MOD;
}
var timeFileName = $"{ModId}.txt";
var modTimeFile = Path.Combine(modsRootFolder, timeFileName);
if (!string.IsNullOrWhiteSpace(modTimeFile) && File.Exists(modTimeFile))
{
LastTimeUpdated = ModUtils.GetModLatestTime(modTimeFile);
}
}
catch
{
// do nothing
}
}
}
}

View file

@ -0,0 +1,200 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Utils;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace ServerManagerTool.Lib
{
public class ModDetailList : ObservableCollection<ModDetail>
{
public bool AnyUnknownModTypes
{
get
{
return this.Any(m => !m.IsValidModType);
}
}
public new void Add(ModDetail mod)
{
if (mod == null || this.Any(m => m.ModId.Equals(mod.ModId)))
return;
base.Add(mod);
SetPublishedFileIndex();
}
public void AddRange(ModDetail[] mods)
{
foreach (var mod in mods)
{
if (mod == null || this.Any(m => m.ModId.Equals(mod.ModId)))
continue;
base.Add(mod);
}
SetPublishedFileIndex();
}
public new void Insert(int index, ModDetail mod)
{
if (mod == null || this.Any(m => m.ModId.Equals(mod.ModId)))
return;
base.Insert(index, mod);
SetPublishedFileIndex();
}
public void Move(ModDetail mod, int newIndex)
{
if (mod == null)
return;
var index = base.IndexOf(mod);
if (index <= 0)
return;
base.Move(index, newIndex);
SetPublishedFileIndex();
}
public void MoveDown(ModDetail mod)
{
if (mod == null)
return;
var index = base.IndexOf(mod);
if (index >= base.Count - 1)
return;
base.Move(index, index + 1);
SetPublishedFileIndex();
}
public void MoveUp(ModDetail mod)
{
if (mod == null)
return;
var index = base.IndexOf(mod);
if (index <= 0)
return;
base.Move(index, index - 1);
SetPublishedFileIndex();
}
public void PopulateExtended(string modsRootFolder)
{
var results = new Dictionary<ModDetail, ModDetailExtended>();
foreach (var mod in this)
{
results.Add(mod, new ModDetailExtended(mod.ModId));
}
Parallel.ForEach(results, kvp => kvp.Value.PopulateExtended(modsRootFolder));
foreach (var kvp in results)
{
kvp.Key.PopulateExtended(kvp.Value);
}
}
public new bool Remove(ModDetail mod)
{
if (mod == null)
return false;
var removed = base.Remove(mod);
SetPublishedFileIndex();
return removed;
}
public void SetPublishedFileIndex()
{
foreach (var mod in this)
{
mod.Index = base.IndexOf(mod) + 1;
mod.IsFirst = false;
mod.IsLast = false;
}
if (this.Count == 0)
return;
this[0].IsFirst = true;
this[base.Count - 1].IsLast = true;
}
public bool GetModStrings(out string mapString, out string modIdString)
{
mapString = null;
modIdString = string.Empty;
var delimiter = "";
foreach (var mod in this)
{
switch (mod.ModType)
{
case ModUtils.MODTYPE_MOD:
default:
modIdString += $"{delimiter}{mod.ModId}";
delimiter = ",";
break;
}
}
return true;
}
public static ModDetailList GetModDetails(List<string> modIdList, string modsRootFolder, WorkshopFileList workshopFiles, PublishedFileDetailsResponse response)
{
var result = new ModDetailList();
if (modIdList != null)
{
foreach (var modId in modIdList)
{
var temp = workshopFiles?.FirstOrDefault(w => w.WorkshopId.Equals(modId));
result.Add(new ModDetail()
{
AppId = temp?.AppId ?? string.Empty,
ModId = modId,
TimeUpdated = -1,
Title = temp?.Title ?? "Mod name not available",
IsValid = false,
});
}
}
if (response?.publishedfiledetails != null)
{
foreach (var item in result)
{
var temp = response.publishedfiledetails.FirstOrDefault(w => w.publishedfileid.Equals(item.ModId));
if (temp != null)
{
item.AppId = temp?.creator_app_id ?? string.Empty;
item.ModId = temp?.publishedfileid ?? item.ModId;
item.TimeUpdated = temp?.time_updated ?? item.TimeUpdated;
item.Title = temp?.title ?? item.Title;
item.IsValid = temp?.creator_app_id != null;
}
}
}
result.SetPublishedFileIndex();
result.PopulateExtended(modsRootFolder);
return result;
}
public override string ToString()
{
return $"{nameof(ModDetailList)} - {Count}";
}
}
}

View file

@ -0,0 +1,34 @@
using System.Windows;
namespace ServerManagerTool.Lib
{
public class PlayerListParameters : DependencyObject
{
public static readonly DependencyProperty ProfileNameProperty = DependencyProperty.Register(nameof(ProfileName), typeof(string), typeof(PlayerListParameters), new PropertyMetadata(string.Empty));
public static readonly DependencyProperty MaxPlayersProperty = DependencyProperty.Register(nameof(MaxPlayers), typeof(int), typeof(PlayerListParameters), new PropertyMetadata(0));
public string ProfileName
{
get { return (string)GetValue(ProfileNameProperty); }
set { SetValue(ProfileNameProperty, value); }
}
public string ProfileId { get; set; }
public string InstallDirectory { get; set; }
public string GameFile { get; set; }
public Server Server { get; set; }
public Rect WindowExtents { get; set; }
public string WindowTitle { get; set; }
public int MaxPlayers
{
get { return (int)GetValue(MaxPlayersProperty); }
set { SetValue(MaxPlayersProperty, value); }
}
}
}

View file

@ -0,0 +1,32 @@
using System.Net;
using System.Windows;
namespace ServerManagerTool.Lib
{
public class RconParameters : PlayerListParameters
{
public string RconHost { get; set; }
public IPAddress RconHostIP
{
get
{
try
{
var ipAddresses = Dns.GetHostAddresses(RconHost);
if (ipAddresses.Length > 0)
return ipAddresses[0].MapToIPv4();
}
catch {}
return IPAddress.None;
}
}
public int RconPort { get; set; }
public string RconPassword { get; set; }
public double PlayerListWidth { get; set; }
}
}

View file

@ -0,0 +1,13 @@
using ServerManagerTool.Common.Attibutes;
using ServerManagerTool.Enums;
namespace ServerManagerTool.Lib
{
public class IniFileEntryAttribute : BaseIniFileEntryAttribute
{
public IniFileEntryAttribute(IniFiles file, IniSections section, ServerProfileCategory category, string key = "")
: base(file, section, category, key)
{
}
}
}

View file

@ -0,0 +1,9 @@
namespace ServerManagerTool.Lib
{
public enum IniFiles
{
Engine,
Game,
ServerSettings,
}
}

View file

@ -0,0 +1,20 @@
namespace ServerManagerTool.Lib
{
public enum IniSections
{
// Engine.ini
Engine_OnlineSubsystem,
Engine_OnlineSubsystemSteam,
Engine_URL,
// Game.ini
Game_GameSession,
Game_RconPlugin,
// ServerSettings.ini
ServerSettings_ServerSettings,
// Misc
Custom,
}
}

View file

@ -0,0 +1,46 @@
using ServerManagerTool.Common.Serialization;
using System;
using System.Collections.Generic;
namespace ServerManagerTool.Lib
{
public class SystemIniFile : BaseSystemIniFile
{
public static readonly Dictionary<Enum, string> IniFileNames = new Dictionary<Enum, string>
{
{ IniFiles.Engine, Config.Default.ServerEngineConfigFile },
{ IniFiles.Game, Config.Default.ServerGameConfigFile },
{ IniFiles.ServerSettings, Config.Default.ServerSettingsConfigFile },
};
public static readonly Dictionary<Enum, string> IniSectionNames = new Dictionary<Enum, string>
{
// Engine sections, used by the server manager
{ IniSections.Engine_OnlineSubsystem, "OnlineSubsystem" },
{ IniSections.Engine_OnlineSubsystemSteam, "OnlineSubsystemSteam" },
{ IniSections.Engine_URL, "URL" },
// Engine sections, not used by server manager
// Game sections, used by the server manager
{ IniSections.Game_GameSession, "/Script/Engine.GameSession" },
{ IniSections.Game_RconPlugin, "RconPlugin" },
// Game sections, not used by server manager
// Server Settings sections, used by the server manager
{ IniSections.ServerSettings_ServerSettings, "ServerSettings" },
// Server Settings sections, not used by server manager
};
public override Dictionary<Enum, string> FileNames => IniFileNames;
public override Dictionary<Enum, string> SectionNames => IniSectionNames;
public SystemIniFile(string iniPath)
: base(iniPath)
{
}
}
}

View file

@ -0,0 +1,105 @@
using ServerManagerTool.Common.Lib;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace ServerManagerTool.Lib
{
public class Server : DependencyObject, IDisposable
{
public static readonly DependencyProperty ProfileProperty = DependencyProperty.Register(nameof(Profile), typeof(ServerProfile), typeof(Server), new PropertyMetadata((ServerProfile)null));
public static readonly DependencyProperty RuntimeProperty = DependencyProperty.Register(nameof(Runtime), typeof(ServerRuntime), typeof(Server), new PropertyMetadata((ServerRuntime)null));
public static readonly DependencyProperty SelectedProperty = DependencyProperty.Register(nameof(Selected), typeof(bool), typeof(Server), new PropertyMetadata(false));
public ServerProfile Profile
{
get { return (ServerProfile)GetValue(ProfileProperty); }
protected set { SetValue(ProfileProperty, value); }
}
public ServerRuntime Runtime
{
get { return (ServerRuntime)GetValue(RuntimeProperty); }
protected set { SetValue(RuntimeProperty, value); }
}
public bool Selected
{
get { return (bool)GetValue(SelectedProperty); }
set { SetValue(SelectedProperty, value); }
}
private Server(ServerProfile profile)
{
InitializeFromProfile(profile);
}
public void Dispose()
{
this.Profile.DestroyServerFilesWatcher();
this.Runtime.StatusUpdate -= Runtime_StatusUpdate;
this.Runtime.Dispose();
}
private void Runtime_StatusUpdate(object sender, EventArgs eventArgs)
{
this.Profile.LastInstalledVersion = this.Runtime.Version.ToString();
}
public void ImportFromPath(string path, ServerProfile profile = null)
{
var loadedProfile = ServerProfile.LoadFrom(path, profile);
if (loadedProfile != null)
InitializeFromProfile(loadedProfile);
}
private void InitializeFromProfile(ServerProfile profile)
{
if (profile == null)
return;
this.Profile = profile;
this.Runtime = new ServerRuntime();
this.Runtime.AttachToProfile(this.Profile).Wait();
this.Runtime.StatusUpdate += Runtime_StatusUpdate;
}
public static Server FromPath(string path)
{
var loadedProfile = ServerProfile.LoadFrom(path);
if (loadedProfile == null)
return null;
return new Server(loadedProfile);
}
public static Server FromDefaults()
{
var loadedProfile = ServerProfile.FromDefaults();
if (loadedProfile == null)
return null;
return new Server(loadedProfile);
}
public async Task StartAsync()
{
await this.Runtime.AttachToProfile(this.Profile);
await this.Runtime.StartAsync();
}
public async Task StopAsync()
{
await this.Runtime.StopAsync();
}
public async Task<bool> UpgradeAsync(CancellationToken cancellationToken, bool updateServer, BranchSnapshot branch, bool validate, bool updateMods, ProgressDelegate progressCallback)
{
await this.Runtime.AttachToProfile(this.Profile);
var success = await this.Runtime.UpgradeAsync(cancellationToken, updateServer, branch, validate, updateMods, progressCallback);
this.Profile.LastInstalledVersion = this.Runtime.Version.ToString();
return success;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Windows;
namespace ServerManagerTool.Lib
{
/// <summary>
/// This class is responsible for managing all of the servers the tool knows about.
/// </summary>
public class ServerManager : DependencyObject
{
static ServerManager()
{
ServerManager.Instance = new ServerManager();
}
public static ServerManager Instance
{
get;
private set;
}
public static readonly DependencyProperty ServersProperty = DependencyProperty.Register(nameof(Servers), typeof(SortableObservableCollection<Server>), typeof(ServerManager), new PropertyMetadata(new SortableObservableCollection<Server>()));
public SortableObservableCollection<Server> Servers
{
get { return (SortableObservableCollection<Server>)GetValue(ServersProperty); }
set { SetValue(ServersProperty, value); }
}
public ServerManager()
{
this.Servers.CollectionChanged += Servers_CollectionChanged;
}
void Servers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if(e.Action == NotifyCollectionChangedAction.Remove)
{
foreach(Server server in e.OldItems)
{
server.Dispose();
}
}
}
public int AddFromPath(string path)
{
var server = Server.FromPath(path);
if (server == null)
return this.Servers.Count - 1;
this.Servers.Add(server);
return this.Servers.Count - 1;
}
public int AddNew()
{
var server = Server.FromDefaults();
if (server == null)
return this.Servers.Count - 1;
this.Servers.Add(server);
return this.Servers.Count - 1;
}
public void Remove(Server server, bool deleteProfile)
{
if (server == null)
return;
// save the profile before deleting, just in case something needed has changed
if (server.Profile != null)
server.Profile.Save(false, false, null);
if (deleteProfile)
{
var profileFile = server.Profile?.GetProfileFile();
if (!string.IsNullOrWhiteSpace(profileFile) && File.Exists(profileFile))
{
// set the file permissions
SecurityUtils.SetFileOwnershipForAllUsers(profileFile);
try
{
File.Delete(profileFile);
}
catch (Exception) { }
}
var profileIniDir = server.Profile?.GetProfileConfigDir_Old();
if (!string.IsNullOrWhiteSpace(profileIniDir) && Directory.Exists(profileIniDir))
{
// set the folder permissions
SecurityUtils.SetDirectoryOwnershipForAllUsers(profileIniDir);
try
{
Directory.Delete(profileIniDir, true);
}
catch (Exception) { }
}
}
server.Runtime?.DeleteFirewallRules();
server.Dispose();
this.Servers.Remove(server);
}
public void CheckProfiles()
{
var serverIds = new Dictionary<string, bool>();
foreach (var server in Servers)
{
if (server == null || server.Profile == null)
continue;
while (serverIds.ContainsKey(server.Profile.ProfileID))
{
server.Profile.ResetProfileId();
}
serverIds.Add(server.Profile.ProfileID, true);
}
}
public void SortServers()
{
Servers.Sort(s => s.Profile?.SortKey);
}
}
}

View file

@ -0,0 +1,235 @@
using ConanData;
using NLog;
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib.ViewModel;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace ServerManagerTool.Lib
{
public class ServerPlayers : DependencyObject
{
private const int PLAYER_LIST_INTERVAL = 5000;
public event EventHandler PlayersCollectionUpdated;
public static readonly DependencyProperty PlayersProperty = DependencyProperty.Register(nameof(Players), typeof(SortableObservableCollection<PlayerInfo>), typeof(ServerPlayers), new PropertyMetadata(null));
public static readonly DependencyProperty CountPlayersProperty = DependencyProperty.Register(nameof(CountPlayers), typeof(int), typeof(ServerPlayers), new PropertyMetadata(0));
public static readonly DependencyProperty CountInvalidPlayersProperty = DependencyProperty.Register(nameof(CountInvalidPlayers), typeof(int), typeof(ServerPlayers), new PropertyMetadata(0));
public static readonly DependencyProperty CountOnlinePlayersProperty = DependencyProperty.Register(nameof(CountOnlinePlayers), typeof(int), typeof(ServerPlayers), new PropertyMetadata(0));
private readonly ConcurrentDictionary<string, PlayerInfo> _players = new ConcurrentDictionary<string, PlayerInfo>();
private readonly object _updatePlayerCollectionLock = new object();
private CancellationTokenSource _cancellationTokenSource = null;
private PlayerListParameters _playerListParameters;
private Logger _allLogger;
private Logger _eventLogger;
private Logger _debugLogger;
private Logger _errorLogger;
private bool _disposed = false;
public ServerPlayers(PlayerListParameters parameters)
{
this.Players = new SortableObservableCollection<PlayerInfo>();
_playerListParameters = parameters;
_allLogger = App.GetProfileLogger(_playerListParameters.ProfileId, "PlayerList_All", LogLevel.Info, LogLevel.Info);
_eventLogger = App.GetProfileLogger(_playerListParameters.ProfileId, "PlayerList_Event", LogLevel.Info, LogLevel.Info);
_debugLogger = App.GetProfileLogger(_playerListParameters.ProfileId, "PlayerList_Debug", LogLevel.Trace, LogLevel.Debug);
_errorLogger = App.GetProfileLogger(_playerListParameters.ProfileId, "PlayerList_Error", LogLevel.Error, LogLevel.Fatal);
UpdatePlayersAsync().DoNotWait();
}
public void Dispose()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
_disposed = true;
}
public SortableObservableCollection<PlayerInfo> Players
{
get { return (SortableObservableCollection<PlayerInfo>)GetValue(PlayersProperty); }
set { SetValue(PlayersProperty, value); }
}
public int CountPlayers
{
get { return (int)GetValue(CountPlayersProperty); }
set { SetValue(CountPlayersProperty, value); }
}
public int CountInvalidPlayers
{
get { return (int)GetValue(CountInvalidPlayersProperty); }
set { SetValue(CountInvalidPlayersProperty, value); }
}
public int CountOnlinePlayers
{
get { return (int)GetValue(CountOnlinePlayersProperty); }
set { SetValue(CountOnlinePlayersProperty, value); }
}
private void LogEvent(LogEventType eventType, string message)
{
switch (eventType)
{
case LogEventType.All:
_allLogger?.Info(message);
return;
case LogEventType.Event:
_eventLogger?.Info(message);
return;
}
}
protected void OnPlayerCollectionUpdated()
{
PlayersCollectionUpdated?.Invoke(this, EventArgs.Empty);
}
private async Task UpdatePlayersAsync()
{
if (this._disposed)
return;
_cancellationTokenSource = new CancellationTokenSource();
var token = _cancellationTokenSource.Token;
await UpdatePlayerDetailsAsync(_cancellationTokenSource.Token);
var cancelled = _cancellationTokenSource.IsCancellationRequested;
if (!cancelled)
{
await TaskUtils.RunOnUIThreadAsync(() =>
{
UpdatePlayerCollection();
});
}
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
if (!cancelled)
{
await Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t => UpdatePlayersAsync());
}
}
private async Task UpdatePlayerDetailsAsync(CancellationToken token)
{
if (this._disposed)
return;
if (string.IsNullOrWhiteSpace(_playerListParameters.GameFile) || !File.Exists(_playerListParameters.GameFile))
return;
var savedPath = ServerProfile.GetProfileSavePath(_playerListParameters.InstallDirectory);
DataContainer dataContainer = null;
try
{
// load the player data from the files.
dataContainer = await DataContainer.CreateAsync(_playerListParameters.GameFile);
}
catch (Exception ex)
{
_errorLogger?.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: CreateAsync. {ex.Message}\r\n{ex.StackTrace}");
return;
}
if (token.IsCancellationRequested)
return;
await Task.Run(() =>
{
// update the player data with the latest update value from the players collection
foreach (var playerData in dataContainer.Players)
{
var id = playerData.PlayerId;
if (string.IsNullOrWhiteSpace(id))
continue;
this._players.TryGetValue(id, out PlayerInfo player);
player?.UpdatePlatformData(playerData);
}
}, token);
if (token.IsCancellationRequested)
return;
var totalPlayers = dataContainer.Players.Count;
foreach (var playerData in dataContainer.Players)
{
token.ThrowIfCancellationRequested();
await Task.Run(async () =>
{
var id = playerData.PlayerId;
if (!string.IsNullOrWhiteSpace(id))
{
var validPlayer = new PlayerInfo()
{
PlayerId = id,
PlayerName = playerData.PlayerName,
IsValid = true,
};
this._players.AddOrUpdate(id, validPlayer, (k, v) => { v.PlayerName = playerData.PlayerName; v.IsValid = true; return v; });
}
else
{
_debugLogger?.Debug($"{nameof(UpdatePlayerDetailsAsync)} - Error: corrupted profile.\r\n{playerData.CharacterId}.");
}
if (this._players.TryGetValue(id, out PlayerInfo player) && player != null)
{
player.UpdateData(playerData);
await TaskUtils.RunOnUIThreadAsync(() =>
{
player.IsWhitelisted = _playerListParameters?.Server?.Profile?.ServerFilesWhitelisted?.Any(u => u.PlayerId.Equals(player.PlayerId, StringComparison.OrdinalIgnoreCase)) ?? false;
});
}
}, token);
}
if (token.IsCancellationRequested)
return;
// remove any players that do not have a player record.
var droppedPlayers = this._players.Values.Where(p => dataContainer.Players.FirstOrDefault(pd => pd.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null).ToArray();
foreach (var droppedPlayer in droppedPlayers)
{
_players.TryRemove(droppedPlayer.PlayerId, out PlayerInfo player);
}
}
private void UpdatePlayerCollection()
{
lock (_updatePlayerCollectionLock)
{
this.Players = new SortableObservableCollection<PlayerInfo>(_players.Values);
this.CountPlayers = this.Players.Count;
this.CountInvalidPlayers = this.Players.Count(p => !p.IsValid);
this.CountOnlinePlayers = this.Players.Count(p => p.IsOnline);
OnPlayerCollectionUpdated();
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,100 @@
using ServerManagerTool.Utils;
using System;
using System.Collections.Generic;
using System.Net;
namespace ServerManagerTool.Lib
{
public class ServerProfileSnapshot
{
public string ProfileId;
public string ProfileName;
public string ServerName;
public string InstallDirectory;
public string GameFile;
public string AdminPassword;
public string ServerIP;
public int ServerPort;
public int ServerPeerPort;
public int QueryPort;
public string ServerMap;
public List<string> ServerModIds;
public bool RconEnabled;
public int RconPort;
public string RconPassword;
public int MaxPlayerCount;
public string MOTD;
public bool MOTDIntervalEnabled;
public int MOTDInterval;
public string BranchName;
public string BranchPassword;
public string SchedulerKey;
public bool EnableAutoBackup;
public bool EnableAutoUpdate;
public bool EnableAutoShutdown1;
public bool RestartAfterShutdown1;
public bool UpdateAfterShutdown1;
public bool EnableAutoShutdown2;
public bool RestartAfterShutdown2;
public bool UpdateAfterShutdown2;
public bool AutoRestartIfShutdown;
public bool ServerUpdated;
public string LastInstalledVersion;
public DateTime LastStarted;
public static ServerProfileSnapshot Create(ServerProfile profile)
{
return new ServerProfileSnapshot
{
ProfileId = profile.ProfileID,
ProfileName = profile.ProfileName,
ServerName = profile.ServerName,
InstallDirectory = profile.InstallDirectory,
GameFile = profile.GetServerWorldFile(),
AdminPassword = profile.AdminPassword,
ServerIP = string.IsNullOrWhiteSpace(profile.ServerIP) ? IPAddress.Loopback.ToString() : profile.ServerIP.Trim(),
ServerPort = profile.ServerPort,
ServerPeerPort = profile.ServerPeerPort,
QueryPort = profile.QueryPort,
ServerMap = ServerProfile.GetProfileMapName(profile),
ServerModIds = ModUtils.GetModIdList(profile.ServerModIds),
RconEnabled = profile.RconEnabled,
RconPort = profile.RconPort,
RconPassword = profile.RconPassword,
MaxPlayerCount = profile.MaxPlayers,
MOTD = profile.MOTD,
MOTDIntervalEnabled = profile.MOTDIntervalEnabled && !string.IsNullOrWhiteSpace(profile.MOTD),
MOTDInterval = Math.Max(1, Math.Min(int.MaxValue, profile.MOTDInterval)),
BranchName = profile.BranchName,
BranchPassword = profile.BranchPassword,
SchedulerKey = profile.GetProfileKey(),
EnableAutoBackup = profile.EnableAutoBackup,
EnableAutoUpdate = profile.EnableAutoUpdate,
EnableAutoShutdown1 = profile.EnableAutoShutdown1,
RestartAfterShutdown1 = profile.RestartAfterShutdown1,
UpdateAfterShutdown1 = profile.UpdateAfterShutdown1,
EnableAutoShutdown2 = profile.EnableAutoShutdown2,
RestartAfterShutdown2 = profile.RestartAfterShutdown2,
UpdateAfterShutdown2 = profile.UpdateAfterShutdown2,
AutoRestartIfShutdown = profile.AutoRestartIfShutdown,
ServerUpdated = false,
LastInstalledVersion = profile.LastInstalledVersion ?? new Version(0, 0).ToString(),
LastStarted = profile.LastStarted,
};
}
public void Update(ServerProfile profile)
{
profile.LastInstalledVersion = LastInstalledVersion;
profile.LastStarted = LastStarted;
}
}
}

View file

@ -0,0 +1,475 @@
using ConanData;
using NLog;
using ServerManagerTool.Common.Enums;
using ServerManagerTool.Common.Interfaces;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Model;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using ServerManagerTool.Lib.ViewModel;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Lib
{
public class ServerRcon : DependencyObject, IAsyncDisposable
{
private const int PLAYER_LIST_INTERVAL = 5000;
private const string NoResponseMatch = "Server received, But no response!!";
public const string NoResponseOutput = "NO_RESPONSE";
public const string RCON_COMMAND_BROADCAST = "broadcast";
public const string RCON_COMMAND_LISTPLAYERS = "#managerplayerlist#";
public event EventHandler PlayersCollectionUpdated;
public static readonly DependencyProperty PlayersProperty = DependencyProperty.Register(nameof(Players), typeof(SortableObservableCollection<PlayerInfo>), typeof(ServerRcon), new PropertyMetadata(null));
public static readonly DependencyProperty CountPlayersProperty = DependencyProperty.Register(nameof(CountPlayers), typeof(int), typeof(ServerRcon), new PropertyMetadata(0));
public static readonly DependencyProperty CountInvalidPlayersProperty = DependencyProperty.Register(nameof(CountInvalidPlayers), typeof(int), typeof(ServerRcon), new PropertyMetadata(0));
public static readonly DependencyProperty CountOnlinePlayersProperty = DependencyProperty.Register(nameof(CountOnlinePlayers), typeof(int), typeof(ServerRcon), new PropertyMetadata(0));
private static readonly ConcurrentDictionary<string, bool> locks = new ConcurrentDictionary<string, bool>();
private static readonly char[] lineSplitChars = new char[] { '\n' };
private static readonly char[] argsSplitChars = new char[] { ' ' };
private readonly ActionQueue _commandProcessor = new ActionQueue(TaskScheduler.Default);
private readonly ActionQueue _outputProcessor = new ActionQueue(TaskScheduler.FromCurrentSynchronizationContext());
private readonly List<CommandListener> _commandListeners = new List<CommandListener>();
private RconParameters _rconParameters;
private QueryMaster.Rcon _console;
private int maxCommandRetries = 3;
private readonly ConcurrentDictionary<string, PlayerInfo> _players = new ConcurrentDictionary<string, PlayerInfo>();
private readonly object _updatePlayerCollectionLock = new object();
private CancellationTokenSource _cancellationTokenSource = null;
private Logger _chatLogger;
private Logger _allLogger;
private Logger _eventLogger;
private Logger _debugLogger;
private Logger _errorLogger;
private bool _disposed = false;
public ServerRcon(RconParameters parameters)
{
this._rconParameters = parameters;
this.Players = new SortableObservableCollection<PlayerInfo>();
_allLogger = App.GetProfileLogger(this._rconParameters.ProfileId, "Rcon_All", LogLevel.Info, LogLevel.Info);
_chatLogger = App.GetProfileLogger(this._rconParameters.ProfileId, "Rcon_Chat", LogLevel.Info, LogLevel.Info);
_eventLogger = App.GetProfileLogger(this._rconParameters.ProfileId, "Rcon_Event", LogLevel.Info, LogLevel.Info);
_debugLogger = App.GetProfileLogger(this._rconParameters.ProfileId, "Rcon_Debug", LogLevel.Trace, LogLevel.Debug);
_errorLogger = App.GetProfileLogger(this._rconParameters.ProfileId, "Rcon_Error", LogLevel.Error, LogLevel.Fatal);
UpdatePlayersAsync().DoNotWait();
}
public async Task DisposeAsync()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
await this._commandProcessor.DisposeAsync();
await this._outputProcessor.DisposeAsync();
for (int index = this._commandListeners.Count - 1; index >= 0; index--)
{
this._commandListeners[index].Dispose();
}
_disposed = true;
}
#region Properties
public SortableObservableCollection<PlayerInfo> Players
{
get { return (SortableObservableCollection<PlayerInfo>)GetValue(PlayersProperty); }
set { SetValue(PlayersProperty, value); }
}
public int CountPlayers
{
get { return (int)GetValue(CountPlayersProperty); }
set { SetValue(CountPlayersProperty, value); }
}
public int CountInvalidPlayers
{
get { return (int)GetValue(CountInvalidPlayersProperty); }
set { SetValue(CountInvalidPlayersProperty, value); }
}
public int CountOnlinePlayers
{
get { return (int)GetValue(CountOnlinePlayersProperty); }
set { SetValue(CountOnlinePlayersProperty, value); }
}
#endregion
#region Methods
private void LogEvent(LogEventType eventType, string message)
{
switch (eventType)
{
case LogEventType.All:
_allLogger?.Info(message);
return;
case LogEventType.Chat:
_chatLogger?.Info(message);
return;
case LogEventType.Event:
_eventLogger?.Info(message);
return;
}
}
internal void OnPlayerCollectionUpdated()
{
PlayersCollectionUpdated?.Invoke(this, EventArgs.Empty);
}
private bool Reconnect()
{
if (this._console != null)
{
this._console.Dispose();
this._console = null;
}
var endpoint = new IPEndPoint(this._rconParameters.RconHostIP, this._rconParameters.RconPort);
var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint, sendTimeOut: 10000, receiveTimeOut: 10000);
this._console = server.GetControl(this._rconParameters.RconPassword);
return this._console != null;
}
public IDisposable RegisterCommandListener(Action<ConsoleCommand> callback)
{
var listener = new CommandListener { Callback = callback, DisposeAction = UnregisterCommandListener };
this._commandListeners.Add(listener);
return listener;
}
private void UnregisterCommandListener(CommandListener listener)
{
this._commandListeners.Remove(listener);
}
#endregion
#region Process Methods
private bool ProcessInput(ConsoleCommand command)
{
try
{
if (!command.suppressCommand)
{
LogEvent(LogEventType.All, command.rawCommand);
}
var args = command.rawCommand.Split(argsSplitChars, 2);
command.command = args[0];
if (args.Length > 1)
{
command.args = args[1];
}
var result = SendCommand(command.rawCommand);
if (result == null)
{
_debugLogger?.Debug($"SendCommand '{command.rawCommand}' do not return any results.");
}
else
{
var lines = result.Split(lineSplitChars, StringSplitOptions.RemoveEmptyEntries).Select(l => l.Trim()).ToArray();
if (!command.suppressOutput)
{
foreach (var line in lines)
{
LogEvent(LogEventType.All, line);
}
}
if (lines.Length == 1 && lines[0].StartsWith(NoResponseMatch))
{
lines[0] = NoResponseOutput;
}
command.lines = lines;
}
command.status = ConsoleStatus.Connected;
this._outputProcessor.PostAction(() => ProcessOutput(command));
return true;
}
catch (Exception ex)
{
_errorLogger?.Error($"Failed to send command '{command.rawCommand}'. {ex.Message}");
command.status = ConsoleStatus.Disconnected;
this._outputProcessor.PostAction(() => ProcessOutput(command));
return false;
}
}
// This is bound to the UI thread
private void ProcessOutput(ConsoleCommand command)
{
//
// Handle results
//
HandleCommand(command);
NotifyCommand(command);
}
public Task<bool> IssueCommand(string userCommand)
{
return this._commandProcessor.PostAction(() => ProcessInput(new ConsoleCommand() { rawCommand = userCommand }));
}
// This is bound to the UI thread
private void HandleCommand(ConsoleCommand command)
{
//
// Perform per-command special processing to extract data
//
if (command?.command?.Equals(RCON_COMMAND_BROADCAST, StringComparison.OrdinalIgnoreCase) ?? false)
{
LogEvent(LogEventType.Chat, command.rawCommand);
command.suppressOutput = true;
}
if (command?.command?.Equals(RCON_COMMAND_LISTPLAYERS, StringComparison.OrdinalIgnoreCase) ?? false)
{
command.suppressCommand = true;
command.suppressOutput = false;
}
}
// This is bound to the UI thread
private void NotifyCommand(ConsoleCommand command)
{
foreach (var listener in _commandListeners)
{
try
{
listener.Callback(command);
}
catch (Exception ex)
{
_errorLogger?.Error("Exception in command listener: {0}\n{1}", ex.Message, ex.StackTrace);
}
}
}
private string SendCommand(string command)
{
const int RETRY_DELAY = 100;
Exception lastException = null;
int retries = 0;
while (retries < maxCommandRetries)
{
if (this._console != null)
{
try
{
return this._console.SendCommand(command);
}
catch (Exception ex)
{
// we will simply retry
lastException = ex;
}
Task.Delay(RETRY_DELAY).Wait();
}
try
{
Reconnect();
}
catch (Exception ex)
{
lastException = ex;
}
retries++;
}
this.maxCommandRetries = 10;
_errorLogger?.Error($"Failed to connect to Rcon at {this._rconParameters.RconHostIP}:{this._rconParameters.RconPort} with {this._rconParameters.RconPassword}. {lastException.Message}");
throw new Exception($"Command failed to send after {maxCommandRetries} attempts. Last exception: {lastException.Message}", lastException);
}
#endregion
private async Task UpdatePlayersAsync()
{
if (this._disposed)
return;
_cancellationTokenSource = new CancellationTokenSource();
var token = _cancellationTokenSource.Token;
var output = await UpdatePlayerDetailsAsync(_cancellationTokenSource.Token);
var cancelled = _cancellationTokenSource.IsCancellationRequested;
if (!cancelled)
{
await TaskUtils.RunOnUIThreadAsync(() =>
{
UpdatePlayerCollection();
var command = new ConsoleCommand() { rawCommand = RCON_COMMAND_LISTPLAYERS, command = RCON_COMMAND_LISTPLAYERS, lines = output };
ProcessOutput(command);
});
}
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
if (!cancelled)
{
await Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t => UpdatePlayersAsync());
}
}
private async Task<List<string>> UpdatePlayerDetailsAsync(CancellationToken token)
{
if (this._disposed)
return new List<string>();
if (string.IsNullOrWhiteSpace(_rconParameters.GameFile) || !File.Exists(_rconParameters.GameFile))
return new List<string>();
var savedPath = ServerProfile.GetProfileSavePath(_rconParameters.InstallDirectory);
DataContainer dataContainer = null;
try
{
// load the player data from the files.
dataContainer = await DataContainer.CreateAsync(_rconParameters.GameFile);
}
catch (Exception ex)
{
_errorLogger?.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: CreateAsync. {ex.Message}\r\n{ex.StackTrace}");
return new List<string>();
}
if (token.IsCancellationRequested)
return new List<string>();
await Task.Run(() =>
{
// update the player data with the latest update value from the players collection
foreach (var playerData in dataContainer.Players)
{
var id = playerData.PlayerId;
if (string.IsNullOrWhiteSpace(id))
continue;
this._players.TryGetValue(id, out PlayerInfo player);
player?.UpdatePlatformData(playerData);
}
}, token);
if (token.IsCancellationRequested)
return new List<string>();
var totalPlayers = dataContainer.Players.Count;
var output = new List<string>();
foreach (var playerData in dataContainer.Players)
{
token.ThrowIfCancellationRequested();
await Task.Run(async () =>
{
var id = playerData.PlayerId;
if (!string.IsNullOrWhiteSpace(id))
{
var validPlayer = new PlayerInfo()
{
PlayerId = id,
PlayerName = playerData.PlayerName,
IsValid = true,
};
this._players.AddOrUpdate(id, validPlayer, (k, v) => { v.PlayerName = playerData.PlayerName; v.IsValid = true; return v; });
}
else
{
_debugLogger?.Debug($"{nameof(UpdatePlayerDetailsAsync)} - Error: corrupted profile.\r\n{playerData.CharacterId}.");
}
if (this._players.TryGetValue(id, out PlayerInfo player) && player != null)
{
if (player.IsOnline != playerData.Online)
{
if (playerData.Online)
{
var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Join") ?? "Player '{0}' joined the game.";
var message = string.Format(messageFormat, playerData.CharacterName);
output.Add(message);
LogEvent(LogEventType.Event, message);
LogEvent(LogEventType.All, message);
}
else
{
var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Leave") ?? "Player '{0}' left the game.";
var message = string.Format(messageFormat, playerData.CharacterName);
output.Add(message);
LogEvent(LogEventType.Event, message);
LogEvent(LogEventType.All, message);
}
}
player.UpdateData(playerData);
await TaskUtils.RunOnUIThreadAsync(() =>
{
player.IsWhitelisted = _rconParameters?.Server?.Profile?.ServerFilesWhitelisted?.Any(u => u.PlayerId.Equals(player.PlayerId, StringComparison.OrdinalIgnoreCase)) ?? false;
});
}
}, token);
}
if (token.IsCancellationRequested)
return new List<string>();
// remove any players that do not have a player record.
var droppedPlayers = this._players.Values.Where(p => dataContainer.Players.FirstOrDefault(pd => pd.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null).ToArray();
foreach (var droppedPlayer in droppedPlayers)
{
_players.TryRemove(droppedPlayer.PlayerId, out PlayerInfo player);
}
return output;
}
private void UpdatePlayerCollection()
{
lock (_updatePlayerCollectionLock)
{
this.Players = new SortableObservableCollection<PlayerInfo>(_players.Values);
this.CountPlayers = this.Players.Count;
this.CountInvalidPlayers = this.Players.Count(p => !p.IsValid);
this.CountOnlinePlayers = this.Players.Count(p => p.IsOnline);
OnPlayerCollectionUpdated();
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
using ServerManagerTool.Enums;
using System.Collections.ObjectModel;
using System.Diagnostics;
namespace ServerManagerTool.Lib
{
public struct ServerStatusUpdate
{
public Process Process;
public WatcherServerStatus Status;
public QueryMaster.ServerInfo ServerInfo;
public int OnlinePlayerCount;
}
}

View file

@ -0,0 +1,24 @@
using ServerManagerTool.Common.Interfaces;
using System;
using System.Net;
using System.Threading.Tasks;
namespace ServerManagerTool.Lib
{
public class ServerStatusUpdateRegistration : IAsyncDisposable
{
public string InstallDirectory;
public IPEndPoint LocalEndpoint;
public IPEndPoint SteamEndpoint;
public Action<IAsyncDisposable, ServerStatusUpdate> UpdateCallback;
public Func<Task> UnregisterAction;
public string ProfileId;
public string GameFile;
public async Task DisposeAsync()
{
await UnregisterAction();
}
}
}

View file

@ -0,0 +1,353 @@
using ConanData;
using NLog;
using ServerManagerTool.Common.Interfaces;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace ServerManagerTool.Lib
{
public class ServerStatusWatcher
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly List<ServerStatusUpdateRegistration> _serverRegistrations = new List<ServerStatusUpdateRegistration>();
private readonly ActionBlock<Func<Task>> _eventQueue;
private readonly Dictionary<string, DateTime> _nextExternalStatusQuery = new Dictionary<string, DateTime>();
static ServerStatusWatcher()
{
Instance = new ServerStatusWatcher();
}
private ServerStatusWatcher()
{
_eventQueue = new ActionBlock<Func<Task>>(async f => await f.Invoke(), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 });
_eventQueue.Post(DoLocalUpdate);
}
public static ServerStatusWatcher Instance
{
get;
private set;
}
public IAsyncDisposable RegisterForUpdates(string installDirectory, string profileId, string gameFile, IPEndPoint localEndpoint, IPEndPoint steamEndpoint, Action<IAsyncDisposable, ServerStatusUpdate> updateCallback)
{
var registration = new ServerStatusUpdateRegistration
{
InstallDirectory = installDirectory,
ProfileId = profileId,
GameFile = gameFile,
LocalEndpoint = localEndpoint,
SteamEndpoint = steamEndpoint,
UpdateCallback = updateCallback,
};
registration.UnregisterAction = async () =>
{
var tcs = new TaskCompletionSource<bool>();
_eventQueue.Post(() =>
{
if(_serverRegistrations.Contains(registration))
{
Logger.Debug($"{nameof(RegisterForUpdates)} Removing registration for L:{registration.LocalEndpoint} S:{registration.SteamEndpoint}");
_serverRegistrations.Remove(registration);
}
tcs.TrySetResult(true);
return Task.FromResult(true);
});
await tcs.Task;
};
_eventQueue.Post(() =>
{
if (!_serverRegistrations.Contains(registration))
{
Logger.Debug($"{nameof(RegisterForUpdates)} Adding registration for L:{registration.LocalEndpoint} S:{registration.SteamEndpoint}");
_serverRegistrations.Add(registration);
var registrationKey = registration.SteamEndpoint.ToString();
_nextExternalStatusQuery[registrationKey] = DateTime.MinValue;
}
return Task.FromResult(true);
}
);
return registration;
}
private static ServerProcessStatus GetServerProcessStatus(ServerStatusUpdateRegistration updateContext, out Process serverProcess)
{
serverProcess = null;
if (String.IsNullOrWhiteSpace(updateContext.InstallDirectory))
{
return ServerProcessStatus.NotInstalled;
}
var serverExePath = Path.Combine(updateContext.InstallDirectory, Config.Default.ServerBinaryRelativePath, Config.Default.ServerExeFile);
if(!File.Exists(serverExePath))
{
return ServerProcessStatus.NotInstalled;
}
//
// The server appears to be installed, now determine if it is running or stopped.
//
try
{
foreach (var process in Process.GetProcessesByName(Config.Default.ServerProcessName))
{
var commandLine = ProcessUtils.GetCommandLineForProcess(process.Id)?.ToLower();
if (commandLine != null && commandLine.Contains(updateContext.InstallDirectory.ToLower()) && commandLine.Contains(Config.Default.ServerExeFile.ToLower()))
{
// Does this match our server exe and port?
var serverArgMatch = String.Format(Config.Default.ServerCommandLineArgsPortMatchFormat, updateContext.LocalEndpoint.Port).ToLower();
if (commandLine.Contains(serverArgMatch))
{
// Was an IP set on it?
var anyIpArgMatch = String.Format(Config.Default.ServerCommandLineArgsIPMatchFormat, String.Empty).ToLower();
if (commandLine.Contains(anyIpArgMatch))
{
// If we have a specific IP, check for it.
var ipArgMatch = String.Format(Config.Default.ServerCommandLineArgsIPMatchFormat, updateContext.LocalEndpoint.Address.ToString()).ToLower();
if (!commandLine.Contains(ipArgMatch))
{
// Specific IP set didn't match
continue;
}
// Specific IP matched
}
// Either specific IP matched or no specific IP was set and we will claim this is ours.
process.EnableRaisingEvents = true;
if (process.HasExited)
{
return ServerProcessStatus.Stopped;
}
serverProcess = process;
return ServerProcessStatus.Running;
}
}
}
}
catch(Exception ex)
{
Logger.Error($"{nameof(GetServerProcessStatus)}. {ex.Message}\r\n{ex.StackTrace}");
}
return ServerProcessStatus.Stopped;
}
private async Task DoLocalUpdate()
{
try
{
foreach (var registration in this._serverRegistrations)
{
ServerStatusUpdate statusUpdate = new ServerStatusUpdate();
try
{
Logger.Info($"{nameof(DoLocalUpdate)} Start: {registration.LocalEndpoint}");
statusUpdate = await GenerateServerStatusUpdateAsync(registration);
PostServerStatusUpdate(registration, registration.UpdateCallback, statusUpdate);
}
catch (Exception ex)
{
// We don't want to stop other registration queries or break the ActionBlock
Logger.Error($"{nameof(DoLocalUpdate)} - Exception in local update. {ex.Message}\r\n{ex.StackTrace}");
Debugger.Break();
}
finally
{
Logger.Info($"{nameof(DoLocalUpdate)} End: {registration.LocalEndpoint}: {statusUpdate.Status}");
}
}
}
finally
{
Task.Delay(Config.Default.ServerStatusWatcher_LocalStatusQueryDelay).ContinueWith(_ => _eventQueue.Post(DoLocalUpdate)).DoNotWait();
}
}
private void PostServerStatusUpdate(ServerStatusUpdateRegistration registration, Action<IAsyncDisposable, ServerStatusUpdate> callback, ServerStatusUpdate statusUpdate)
{
_eventQueue.Post(() =>
{
if (this._serverRegistrations.Contains(registration))
{
try
{
callback(registration, statusUpdate);
}
catch (Exception ex)
{
DebugUtils.WriteFormatThreadSafeAsync("Exception during local status update callback: {0}\n{1}", ex.Message, ex.StackTrace).DoNotWait();
}
}
return TaskUtils.FinishedTask;
});
}
private async Task<ServerStatusUpdate> GenerateServerStatusUpdateAsync(ServerStatusUpdateRegistration registration)
{
var registrationKey = registration.SteamEndpoint.ToString();
//
// First check the process status
//
var processStatus = GetServerProcessStatus(registration, out Process process);
switch (processStatus)
{
case ServerProcessStatus.NotInstalled:
return new ServerStatusUpdate { Status = WatcherServerStatus.NotInstalled };
case ServerProcessStatus.Stopped:
return new ServerStatusUpdate { Status = WatcherServerStatus.Stopped };
case ServerProcessStatus.Unknown:
return new ServerStatusUpdate { Status = WatcherServerStatus.Unknown };
case ServerProcessStatus.Running:
break;
default:
Debugger.Break();
break;
}
var currentStatus = WatcherServerStatus.Initializing;
//
// If the process was running do we then perform network checks.
//
Logger.Info($"{nameof(GenerateServerStatusUpdateAsync)} Checking server local network status at {registration.LocalEndpoint}");
// get the server information direct from the server using local connection.
GetLocalNetworkStatus(registration.GameFile, registration.LocalEndpoint, out QueryMaster.ServerInfo localInfo, out int onlinePlayerCount);
if (localInfo != null)
{
currentStatus = WatcherServerStatus.RunningLocalCheck;
//
// Now that it's running, we can check the publication status.
//
Logger.Info($"{nameof(GenerateServerStatusUpdateAsync)} Checking server public status direct at {registration.SteamEndpoint}");
// get the server information direct from the server using public connection.
var serverStatus = CheckServerStatusDirect(registration.SteamEndpoint);
// check if the server returned the information.
if (!serverStatus)
{
// server did not return any information
var nextExternalStatusQuery = _nextExternalStatusQuery.ContainsKey(registrationKey) ? _nextExternalStatusQuery[registrationKey] : DateTime.MinValue;
if (DateTime.Now >= nextExternalStatusQuery)
{
currentStatus = WatcherServerStatus.RunningExternalCheck;
if (!string.IsNullOrWhiteSpace(Config.Default.ServerStatusUrlFormat))
{
Logger.Info($"{nameof(GenerateServerStatusUpdateAsync)} Checking server public status via api at {registration.SteamEndpoint}");
// get the server information direct from the server using external connection.
var uri = new Uri(string.Format(Config.Default.ServerStatusUrlFormat, Config.Default.ServerManagerCode, App.Instance.Version, registration.SteamEndpoint.Address, registration.SteamEndpoint.Port));
serverStatus = await NetworkUtils.CheckServerStatusViaAPI(uri, registration.SteamEndpoint);
}
_nextExternalStatusQuery[registrationKey] = DateTime.Now.AddMilliseconds(Config.Default.ServerStatusWatcher_RemoteStatusQueryDelay);
}
}
// check if the server returned the information.
if (serverStatus)
{
currentStatus = WatcherServerStatus.Published;
}
}
var statusUpdate = new ServerStatusUpdate
{
Process = process,
Status = currentStatus,
ServerInfo = localInfo,
OnlinePlayerCount = onlinePlayerCount,
};
return await Task.FromResult(statusUpdate);
}
private static bool GetLocalNetworkStatus(string gameFile, IPEndPoint endpoint, out QueryMaster.ServerInfo serverInfo, out int onlinePlayerCount)
{
serverInfo = null;
onlinePlayerCount = 0;
try
{
using (var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint))
{
try
{
serverInfo = server.GetInfo();
}
catch (Exception)
{
serverInfo = null;
}
}
try
{
// load the player data from the files.
onlinePlayerCount = DataContainer.GetOnlinePlayerCount(gameFile);
}
catch (Exception)
{
onlinePlayerCount = 0;
}
}
catch (SocketException ex)
{
// Common when the server is unreachable. Log and Ignore it.
Logger.Debug($"{nameof(GetLocalNetworkStatus)} failed: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
}
return true;
}
private static bool CheckServerStatusDirect(IPEndPoint endpoint)
{
try
{
QueryMaster.ServerInfo serverInfo;
using (var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint))
{
serverInfo = server.GetInfo();
}
return serverInfo != null;
}
catch (Exception ex)
{
Logger.Debug($"{nameof(CheckServerStatusDirect)} - Failed checking status direct for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
return false;
}
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Lib.ViewModel
{
public class EnumDescriptionTypeConverter : EnumConverter
{
public EnumDescriptionTypeConverter(Type type)
: base(type)
{
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
if (value != null)
{
var strType = value.GetType().Name;
var strVal = value.ToString();
return GlobalizedApplication.Instance.GetResourceString($"{strType}_{strVal}") ?? strVal;
}
return string.Empty;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}

View file

@ -0,0 +1,50 @@
using ServerManagerTool.Utils;
using System;
using System.Globalization;
using System.Windows.Data;
using WPFSharp.Globalizer;
namespace ServerManagerTool.Lib.ViewModel
{
public class MapNameValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
var valueString = value as string;
if (valueString == null)
return string.Empty;
var name = GlobalizedApplication.Instance.GetResourceString(valueString);
if (!string.IsNullOrWhiteSpace(name))
return name;
name = GameData.FriendlyMapNameForClass(valueString, true);
if (!string.IsNullOrWhiteSpace(name))
return name;
var mapName = ModUtils.GetMapName(valueString);
// check if the name is stored in the globalization file
name = GlobalizedApplication.Instance.GetResourceString(mapName);
if (!string.IsNullOrWhiteSpace(name))
return name;
if (!string.IsNullOrWhiteSpace(mapName))
return mapName;
return valueString;
}
catch
{
return value ?? string.Empty;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,160 @@
using ConanData;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ServerManagerTool.Lib.ViewModel
{
public class PlayerInfo : INotifyPropertyChanged
{
public PlayerInfo()
{
PlayerId = string.Empty;
PlayerName = string.Empty;
CharacterName = string.Empty;
IsOnline = false;
IsAdmin = false;
IsWhitelisted = false;
GuildName = string.Empty;
IsValid = true;
LastOnline = null;
PlayerData = null;
}
public string PlayerId
{
get { return Get<string>(); }
set { Set(value); }
}
public string PlayerName
{
get { return Get<string>(); }
set
{
Set(value);
PlatformNameFilterString = value?.ToLower();
}
}
public string PlatformNameFilterString
{
get;
private set;
}
public long CharacterId
{
get { return Get<long>(); }
set { Set(value); }
}
public string CharacterName
{
get { return Get<string>(); }
set
{
Set(value);
CharacterNameFilterString = value?.ToLower();
}
}
public string CharacterNameFilterString
{
get;
private set;
}
public bool IsOnline
{
get { return Get<bool>(); }
set { Set(value); }
}
public bool IsAdmin
{
get { return Get<bool>(); }
set { Set(value); }
}
public bool IsWhitelisted
{
get { return Get<bool>(); }
set { Set(value); }
}
public string GuildName
{
get { return Get<string>(); }
set
{
Set(value);
GuildNameFilterString = value?.ToLower();
}
}
public string GuildNameFilterString
{
get;
private set;
}
public bool IsValid
{
get { return Get<bool>(); }
set { Set(value); }
}
public int? LastOnline
{
get { return Get<int?>(); }
set { Set(value); }
}
public PlayerData PlayerData
{
get { return Get<PlayerData>(); }
set { Set(value); }
}
public void UpdateData(PlayerData playerData)
{
this.PlayerData = playerData;
this.PlayerId = playerData?.PlayerId;
this.CharacterId = playerData?.CharacterId ?? 0L;
this.CharacterName = playerData?.CharacterName;
this.GuildName = playerData?.Guild?.GuildName;
this.IsOnline = playerData?.Online ?? false;
this.LastOnline = playerData?.LastOnline;
}
public void UpdatePlatformData(PlayerData playerData)
{
if (playerData == null)
return;
playerData.PlayerName = PlayerData?.PlayerName;
playerData.LastPlatformUpdateUtc = PlayerData?.LastPlatformUpdateUtc ?? DateTime.MinValue;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private Dictionary<string, object> _properties = new Dictionary<string, object>();
protected T Get<T>([CallerMemberName] string name = null)
{
object value = null;
if (_properties?.TryGetValue(name, out value) ?? false)
return value == null ? default : (T)value;
return default;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void Set<T>(T value, [CallerMemberName] string name = null)
{
if (Equals(value, Get<T>(name)))
return;
if (_properties == null)
_properties = new Dictionary<string, object>();
_properties[name] = value;
OnPropertyChanged(name);
}
#endregion
}
}

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<variable name="logDir" value=""/>
<targets>
<target name="debug"
xsi:type="Debugger"
layout="${time} DEBUG: ${message}"
/>
<target name="error"
xsi:type="Debugger"
layout="${time} ERROR: ${message}"
/>
<target name="debugFile" xsi:type="File"
fileName="${logDir}/ServerManager_Debug.log"
layout="${time} ${message}"
archiveFileName="${logDir}/ServerManager_Debug.{#}.log"
archiveNumbering="DateAndSequence"
archiveEvery="Day"
archiveDateFormat="yyyyMMdd"
maxArchiveFiles="30"
/>
<target name="errorFile" xsi:type="File"
fileName="${logDir}/ServerManager_Error.log"
layout="${time} ${message}"
archiveFileName="${logDir}/ServerManager_Error.{#}.log"
archiveNumbering="DateAndSequence"
archiveEvery="Day"
archiveDateFormat="yyyyMMdd"
maxArchiveFiles="30"
/>
<target name="scripts" xsi:type="File"
fileName="${logDir}/ServerManager_Scripts.log"
layout="${time} ${message}"
archiveFileName="${logDir}/ServerManager_Scripts.{#}.log"
archiveNumbering="DateAndSequence"
archiveEvery="Day"
archiveDateFormat="yyyyMMdd"
maxArchiveFiles="30"
/>
<target name="statuswatcher" xsi:type="File"
fileName="${logDir}/ServerManager_ServerStatus.log"
layout="${time} ${message}"
archiveFileName="${logDir}/ServerManager_ServerStatus.{#}.log"
archiveNumbering="DateAndSequence"
archiveEvery="Day"
archiveDateFormat="yyyyMMdd"
maxArchiveFiles="30"
/>
<target name="taskscheduler" xsi:type="File"
fileName="${logDir}/ServerManager_TaskScheduler.log"
layout="${time} ${message}"
archiveFileName="${logDir}/ServerManager_TaskScheduler.{#}.log"
archiveNumbering="DateAndSequence"
archiveEvery="Day"
archiveDateFormat="yyyyMMdd"
maxArchiveFiles="30"
/>
</targets>
<rules>
<logger enabled="false" name="*" maxlevel="debug" writeTo="debug"/>
<logger enabled="false" name="*" minlevel="error" writeTo="error"/>
<logger enabled="false" name="*" maxlevel="debug" writeTo="debugFile"/>
<logger enabled="true" name="*" minlevel="error" writeTo="errorFile"/>
<logger enabled="true" name="ServerManagerTool.Common.Utils.ScriptUtils" minlevel="info" writeTo="scripts"/>
<logger enabled="true" name="ServerManagerTool.Common.Utils.TaskSchedulerUtils" minlevel="info" writeTo="taskscheduler"/>
<logger enabled="true" name="ServerManagerTool.Lib.ServerStatusWatcher" minlevel="info" writeTo="statuswatcher"/>
</rules>
</nlog>

View file

@ -0,0 +1,55 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Conan Exiles™ Server Manager")]
[assembly: AssemblyDescription("The server manager makes it easy to setup and manage your Conan Exiles™ dedicated servers.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Bletch1971")]
[assembly: AssemblyProduct("Server Managers")]
[assembly: AssemblyCopyright("Copyright © 2015-2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
//where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.None,
//where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
ResourceDictionaryLocation.SourceAssembly
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0")]
[assembly: AssemblyVersion("1.0")]
[assembly: AssemblyFileVersion("1.0.0.1")]

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel node will disable file and registry virtualization.
If you want to utilize File and Registry Virtualization for backward
compatibility then delete the requestedExecutionLevel node.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" ID="Custom" SameSite="site" Unrestricted="true" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of all Windows versions that this application is designed to work with.
Windows will automatically select the most compatible environment.-->
<!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>-->
<!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->
<!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>-->
<!-- If your application is designed to work with Windows 8.1, uncomment the following supportedOS node-->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>-->
</application>
</compatibility>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!-- <dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>-->
</asmv1:assembly>

View file

@ -0,0 +1,55 @@
<Globalization:StyleResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Globalization="clr-namespace:WPFSharp.Globalizer;assembly=WPFSharp.Globalizer"
Name="Default">
<!--<FlowDirection x:Key="FlowDirection_Default">LeftToRight</FlowDirection>
<FlowDirection x:Key="FlowDirection_Reverse">RightToLeft</FlowDirection>-->
<!--<sys:Double x:Key="CommonFontSize">12</sys:Double>-->
<SolidColorBrush x:Key="SolidBackground" Color="White" />
<!--BeigeGradient-->
<LinearGradientBrush x:Key="GradientBackground" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFECE1D4" Offset="1"/>
<GradientStop Color="#FFEAE8E6"/>
</LinearGradientBrush>
<!--Generic control styles-->
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Black" />
</Style>
<!--Named styles-->
<Style x:Key="BorderLabel" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="Height" Value="Auto"/>
<Setter Property="BorderBrush" Value="#FFD8CCBC"/>
<Setter Property="Background" Value="#FFE6DFD8"/>
</Style>
<!--AnnotatedSlider-->
<SolidColorBrush x:Key="SliderThumb.Static.Foreground" Color="#FFE5E5E5"/>
<SolidColorBrush x:Key="SliderThumb.MouseOver.Background" Color="#FFDCECFC"/>
<SolidColorBrush x:Key="SliderThumb.MouseOver.Border" Color="#FF7Eb4EA"/>
<SolidColorBrush x:Key="SliderThumb.Pressed.Background" Color="#FFDAECFC"/>
<SolidColorBrush x:Key="SliderThumb.Pressed.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="SliderThumb.Disabled.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="SliderThumb.Disabled.Border" Color="#FFD9D9D9"/>
<SolidColorBrush x:Key="SliderThumb.Static.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="SliderThumb.Static.Border" Color="#FFACACAC"/>
<SolidColorBrush x:Key="SliderThumb.Track.Border" Color="#FFD6D6D6"/>
<SolidColorBrush x:Key="SliderThumb.Track.BorderBackground" Color="#FFE7EAEA"/>
<SolidColorBrush x:Key="SliderThumb.Grip.Fill" Color="#FFE6DFD8"/>
<SolidColorBrush x:Key="SliderThumb.Tick.Bottom" Color="#FFD8CCBC"/>
<SolidColorBrush x:Key="SliderThumb.Track.Background" Color="#FFBFA786"/>
<SolidColorBrush x:Key="WarningMessage" Color="Red"/>
</Globalization:StyleResourceDictionary>

View file

@ -0,0 +1,183 @@
using ServerManagerTool.Common;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace ServerManagerTool.Utils
{
public static class ModUtils
{
public const string MODTYPE_UNKNOWN = "0";
public const string MODTYPE_MOD = "1";
public const string MODTYPE_MAP = "2";
public static void CopyMod(string sourceFolder, string destinationFolder, string modId, ProgressDelegate progressCallback)
{
if (string.IsNullOrWhiteSpace(sourceFolder))
throw new DirectoryNotFoundException($"Source folder must be specified.");
if (!Directory.Exists(sourceFolder))
throw new DirectoryNotFoundException($"Source folder was not found.\r\n{sourceFolder}");
if (string.IsNullOrWhiteSpace(destinationFolder))
throw new DirectoryNotFoundException($"Destination folder must be specified.");
var modFileName = $"{modId}.pak";
var modFile = IOUtils.NormalizePath(Path.Combine(destinationFolder, modFileName));
var timeFileName = $"{modId}.txt";
var timeFile = IOUtils.NormalizePath(Path.Combine(destinationFolder, timeFileName));
progressCallback?.Invoke(0, "Deleting existing mod files.");
// delete the server mod file.
if (File.Exists(modFile))
File.Delete(modFile);
if (File.Exists(timeFile))
File.Delete(timeFile);
progressCallback?.Invoke(0, "Copying mod files.");
if (string.IsNullOrWhiteSpace(destinationFolder) || !Directory.Exists(destinationFolder))
Directory.CreateDirectory(destinationFolder);
// update the mod files from the cache.
foreach (var sourceFile in Directory.GetFiles(sourceFolder, "*.pak", SearchOption.TopDirectoryOnly))
{
File.Copy(sourceFile, modFile, true);
}
// copy the last updated file.
var fileName = IOUtils.NormalizePath(Path.Combine(sourceFolder, Config.Default.LastUpdatedTimeFile));
if (File.Exists(fileName))
{
progressCallback?.Invoke(0, "Copying mod version file.");
File.Copy(fileName, timeFile, true);
}
}
public static void CreateModListFile(string installDirectory, List<string> modIdList)
{
if (string.IsNullOrWhiteSpace(installDirectory) || !Directory.Exists(installDirectory))
return;
// get the folder/file details
var modRootFolder = GetModRootPath(installDirectory);
var modListFileName = Config.Default.ServerModListFile;
var modListFile = IOUtils.NormalizePath(Path.Combine(modRootFolder, modListFileName));
// check if any mods to write
if (modIdList == null || modIdList.Count == 0)
{
// no mods, check if foldler exists
if (!Directory.Exists(modRootFolder))
return;
// check if the file exists, if so then delete it.
if (File.Exists(modListFile))
File.Delete(modListFile);
}
else
{
// check if the folder exists, if not then create it.
if (!Directory.Exists(modRootFolder))
Directory.CreateDirectory(modRootFolder);
// get the a list of the mod file into include in the mod file
var modFileItems = modIdList.Select(m => $"{m}.pak").ToArray();
// create the mod file.
File.WriteAllLines(modListFile, modFileItems);
}
}
public static string GetLatestModCacheTimeFile(string modId) => IOUtils.NormalizePath(Path.Combine(GetModCachePath(modId), Config.Default.LastUpdatedTimeFile));
public static string GetLatestModTimeFile(string installDirectory, string modId) => IOUtils.NormalizePath(Path.Combine(installDirectory, Config.Default.ServerModsRelativePath, $"{modId}.txt"));
public static string GetMapName(string serverMap)
{
if (string.IsNullOrWhiteSpace(serverMap))
return string.Empty;
return serverMap.Trim();
}
public static string GetModCachePath(string modId) => IOUtils.NormalizePath(Path.Combine(Config.Default.DataPath, CommonConfig.Default.SteamCmdRelativePath, Config.Default.AppSteamWorkshopFolderRelativePath, modId));
public static List<string> GetModIdList(string modIds)
{
if (string.IsNullOrWhiteSpace(modIds))
return new List<string>();
return modIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
}
public static int GetModLatestTime(string timeFile)
{
try
{
if (!File.Exists(timeFile))
return 0;
var value = File.ReadAllText(timeFile);
int unixTime;
return int.TryParse(value, out unixTime) ? unixTime : 0;
}
catch
{
return 0;
}
}
public static string GetModRootPath(string installDirectory) => IOUtils.NormalizePath(Path.Combine(installDirectory, Config.Default.ServerModsRelativePath));
public static string GetModPath(string installDirectory, string modId) => GetModRootPath(installDirectory);
public static string GetSteamManifestFile(string installDirectory) => IOUtils.NormalizePath(Path.Combine(installDirectory, Config.Default.SteamManifestFolderRelativePath, Config.Default.AppSteamManifestFile));
public static string GetSteamWorkshopFile() => IOUtils.NormalizePath(Path.Combine(Config.Default.DataPath, CommonConfig.Default.SteamCmdRelativePath, Config.Default.SteamWorkshopFolderRelativePath, Config.Default.AppSteamWorkshopFile));
public static int GetSteamWorkshopLatestTime(string workshopFile, string modId)
{
try
{
var result = SteamUtils.ReadSteamCmdAppWorkshopFile(workshopFile);
if (result == null)
return 0;
var detail = result.WorkshopItemDetails.FirstOrDefault(v => v.publishedfileid.Equals(modId));
if (detail == null)
return 0;
int unixTime;
return int.TryParse(detail.timeupdated, out unixTime) ? unixTime : 0;
}
catch
{
return 0;
}
}
public static bool IsOfficialMod(string modId)
{
switch (modId)
{
default:
return false;
}
}
public static List<string> ValidateModList(List<string> modIdList)
{
// remove all duplicate mod ids.
var newModIdList = modIdList.Distinct().ToList();
// remove any official mods.
return newModIdList;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>urn:uuid:F9268CD1-3230-46A7-BD1D-023A2620A33B</id>
<title>Conan Server Manager Version Feed</title>
<subtitle>This is the Conan Server Manager beta version feed.</subtitle>
<link href="http://servermanagers.freeforums.net/" />
<updated>2020-11-25T00:00:00Z</updated>
<entry>
<id>urn:uuid:6914861A-2D4E-4F08-89A3-D3D7A77F74C1</id>
<title>1.1.50 (1.1.50.1)</title>
<summary>1.1.50.1</summary>
<link href="" />
<updated>2020-11-25T00:00:00Z</updated>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" style="font-family: Arial, Verdana, Helvetica, Sans-Serif;font-size: .8em;">
<p>
<u style="font-size: .9em;">NEW</u>
<br/>
<ul>
<li>Global Settings - Added option to automatically manage the Public IP, default to True. If disabled, then the Public IP must be updated manually.</li>
</ul>
</p>
</div>
</content>
<author>
<name>bletch</name>
<email>bletch1971@hotmail.com</email>
</author>
</entry>
</feed>

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>

Some files were not shown because too many files have changed in this diff Show more