diff --git a/src/ARKServerManager/App.xaml.cs b/src/ARKServerManager/App.xaml.cs
index c7f0be9e..30b02f45 100644
--- a/src/ARKServerManager/App.xaml.cs
+++ b/src/ARKServerManager/App.xaml.cs
@@ -683,12 +683,12 @@ namespace ServerManagerTool
if (Config.Default.LoggingEnabled)
{
while (!LogManager.IsLoggingEnabled())
- LogManager.EnableLogging();
+ LogManager.ResumeLogging();
}
else
{
while (LogManager.IsLoggingEnabled())
- LogManager.DisableLogging();
+ LogManager.SuspendLogging();
}
Debug.WriteLine($"Logging Enabled: {LogManager.IsLoggingEnabled()}");
diff --git a/src/ARKServerManager/Globalization/en-US/en-US.xaml b/src/ARKServerManager/Globalization/en-US/en-US.xaml
index 68f515f5..0aef798f 100644
--- a/src/ARKServerManager/Globalization/en-US/en-US.xaml
+++ b/src/ARKServerManager/Globalization/en-US/en-US.xaml
@@ -968,14 +968,19 @@
Last Start Time:
Availability:
The status of the server on Steam.
- Unknown
- Set Public IP
- Unavailable
- Waiting for publication
- Available
Players:
The current number of players connected to the server.
+ Unknown
+ Unavailable
+ Server in not accessible
+ LAN Only
+ Server accessible via Local IP only. Port Forwarding, Firewall or CGNAT etc are preventing public access.
+ Available - No Loopback
+ Server accessible via Local IP and Public IP, loopback not available. Server is public but Public IP not accessible via LAN.
+ Available
+ Server accessible via Local IP and Public IP
+
Initializing
Running
Stopped
diff --git a/src/ARKServerManager/Lib/ServerRuntime.cs b/src/ARKServerManager/Lib/ServerRuntime.cs
index 46f0ce75..e96e67bf 100644
--- a/src/ARKServerManager/Lib/ServerRuntime.cs
+++ b/src/ARKServerManager/Lib/ServerRuntime.cs
@@ -163,8 +163,8 @@ namespace ServerManagerTool.Lib
new[] {
ServerProfile.ProfileNameProperty,
ServerProfile.InstallDirectoryProperty,
- ServerProfile.QueryPortProperty,
ServerProfile.ServerPortProperty,
+ ServerProfile.QueryPortProperty,
ServerProfile.ServerIPProperty,
ServerProfile.MaxPlayersProperty,
@@ -196,7 +196,7 @@ namespace ServerManagerTool.Lib
//
if (!ushort.TryParse(this.ProfileSnapshot.QueryPort.ToString(), out _))
{
- Debug.WriteLine($"Port is out of range ({this.ProfileSnapshot.QueryPort})");
+ _logger.Error($"Port is out of range ({this.ProfileSnapshot.QueryPort})");
return;
}
@@ -206,7 +206,7 @@ namespace ServerManagerTool.Lib
// Get the public endpoint for querying Steam
//
steamServerQueryEndPoint = null;
- if (!String.IsNullOrWhiteSpace(Config.Default.MachinePublicIP))
+ if (!string.IsNullOrWhiteSpace(Config.Default.MachinePublicIP))
{
if (IPAddress.TryParse(Config.Default.MachinePublicIP, out IPAddress steamServerIpAddress))
{
@@ -226,7 +226,7 @@ namespace ServerManagerTool.Lib
}
catch (Exception ex)
{
- Debug.WriteLine("Failed to resolve DNS address {0}: {1}\r\n{2}", Config.Default.MachinePublicIP, ex.Message, ex.StackTrace);
+ _logger.Error($"{nameof(GetServerEndpoints)} - Failed to resolve DNS address {Config.Default.MachinePublicIP}. {ex.Message}\r\n{ex.StackTrace}");
}
}
}
@@ -238,7 +238,7 @@ namespace ServerManagerTool.Lib
private void ProcessStatusUpdate(IAsyncDisposable registration, ServerStatusUpdate update)
{
- if(!Object.ReferenceEquals(registration, this.updateRegistration))
+ if (!ReferenceEquals(registration, this.updateRegistration))
{
return;
}
@@ -246,64 +246,90 @@ namespace ServerManagerTool.Lib
TaskUtils.RunOnUIThreadAsync(() =>
{
var oldStatus = this.Status;
+ var oldAvailability = this.Availability;
+
switch (update.Status)
{
+ case WatcherServerStatus.Unknown:
+ if (oldStatus != ServerStatus.Updating)
+ UpdateServerStatus(ServerStatus.Unknown, AvailabilityStatus.Unknown, false);
+
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ break;
+
case WatcherServerStatus.NotInstalled:
if (oldStatus != ServerStatus.Updating)
UpdateServerStatus(ServerStatus.Uninstalled, AvailabilityStatus.Unavailable, false);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
- break;
- case WatcherServerStatus.Initializing:
- if (oldStatus != ServerStatus.Stopping)
- UpdateServerStatus(ServerStatus.Initializing, AvailabilityStatus.Unavailable, oldStatus != ServerStatus.Initializing && oldStatus != ServerStatus.Unknown);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
case WatcherServerStatus.Stopped:
if (oldStatus != ServerStatus.Updating)
UpdateServerStatus(ServerStatus.Stopped, AvailabilityStatus.Unavailable, oldStatus == ServerStatus.Initializing || oldStatus == ServerStatus.Running || oldStatus == ServerStatus.Stopping);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
- case WatcherServerStatus.Unknown:
- if (oldStatus != ServerStatus.Updating)
- UpdateServerStatus(ServerStatus.Unknown, AvailabilityStatus.Unknown, false);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ case WatcherServerStatus.Initializing:
+ if (oldStatus != ServerStatus.Stopping)
+ UpdateServerStatus(ServerStatus.Initializing, AvailabilityStatus.Unavailable, oldStatus != ServerStatus.Initializing && oldStatus != ServerStatus.Unknown);
+
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
case WatcherServerStatus.RunningLocalCheck:
if (oldStatus != ServerStatus.Stopping)
{
- UpdateServerStatus(ServerStatus.Running, this.Availability != AvailabilityStatus.Available ? AvailabilityStatus.Waiting : this.Availability, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
- if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Start();
+ UpdateServerStatus(ServerStatus.Running, oldAvailability != AvailabilityStatus.Available ? AvailabilityStatus.LocalOnly : AvailabilityStatus.Available, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
+
+ if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Start();
}
else
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ {
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ }
break;
case WatcherServerStatus.RunningExternalCheck:
if (oldStatus != ServerStatus.Stopping)
{
- UpdateServerStatus(ServerStatus.Running, AvailabilityStatus.Waiting, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
- if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Start();
+ UpdateServerStatus(ServerStatus.Running, AvailabilityStatus.PublicOnly, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
+
+ if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Start();
}
else
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ {
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ }
break;
case WatcherServerStatus.Published:
if (oldStatus != ServerStatus.Stopping)
{
UpdateServerStatus(ServerStatus.Running, AvailabilityStatus.Available, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
- if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Start();
+
+ if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Start();
}
else
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ {
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ }
break;
default:
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
}
@@ -316,7 +342,7 @@ namespace ServerManagerTool.Lib
if (match.Success && match.Groups.Count >= 2)
{
var serverVersion = match.Groups[1].Value;
- if (!String.IsNullOrWhiteSpace(serverVersion) && Version.TryParse(serverVersion, out Version temp))
+ if (!string.IsNullOrWhiteSpace(serverVersion) && Version.TryParse(serverVersion, out Version temp))
{
this.Version = temp;
}
@@ -512,7 +538,7 @@ namespace ServerManagerTool.Lib
{
if (updateServer && !Environment.Is64BitOperatingSystem)
{
- var result = MessageBox.Show("The ARK server requires a 64-bit operating system to run. Your operating system is 32-bit and therefore the Ark Server Manager will be unable to start the server, but you may still install it or load and save profiles and settings files for use on other machines.\r\n\r\nDo you wish to continue?", "64-bit OS Required", MessageBoxButton.YesNo, MessageBoxImage.Warning);
+ var result = MessageBox.Show("The server requires a 64-bit operating system to run. Your operating system is 32-bit and therefore the Ark Server Manager will be unable to start the server, but you may still install it or load and save profiles and settings files for use on other machines.\r\n\r\nDo you wish to continue?", "64-bit OS Required", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
{
return false;
@@ -583,10 +609,10 @@ namespace ServerManagerTool.Lib
int count = 0;
await Task.Run(() =>
ServerApp.DirectoryCopy(cacheFolder, installationFolder, true, Config.Default.AutoUpdate_UseSmartCopy, (p, m, n) =>
- {
- count++;
- progressCallback?.Invoke(0, ".", count % DIRECTORIES_PER_LINE == 0);
- }), cancellationToken);
+ {
+ count++;
+ progressCallback?.Invoke(0, ".", count % DIRECTORIES_PER_LINE == 0);
+ }), cancellationToken);
}
}
@@ -840,7 +866,7 @@ namespace ServerManagerTool.Lib
// check if the mod needs to be copied, or force the copy.
if (Config.Default.ServerUpdate_ForceCopyMods)
{
- progressCallback?.Invoke(0, $"{SteamCmdUpdater.OUTPUT_PREFIX} Forcing mod copy - ASM setting is TRUE.");
+ progressCallback?.Invoke(0, $"{SteamCmdUpdater.OUTPUT_PREFIX} Forcing mod copy - Server Manager setting is TRUE.");
}
else
{
diff --git a/src/ARKServerManager/Lib/ServerStatusWatcher.cs b/src/ARKServerManager/Lib/ServerStatusWatcher.cs
index 057c8c80..64de877f 100644
--- a/src/ARKServerManager/Lib/ServerStatusWatcher.cs
+++ b/src/ARKServerManager/Lib/ServerStatusWatcher.cs
@@ -1,4 +1,6 @@
-using NLog;
+using Newtonsoft.Json.Linq;
+using NLog;
+using QueryMaster;
using ServerManagerTool.Common.Enums;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
@@ -22,15 +24,15 @@ namespace ServerManagerTool.Lib
private readonly ActionBlock> _eventQueue;
private readonly Dictionary _nextExternalStatusQuery = new Dictionary();
+ static ServerStatusWatcher()
+ {
+ Instance = new ServerStatusWatcher();
+ }
+
private ServerStatusWatcher()
{
_eventQueue = new ActionBlock>(async f => await f.Invoke(), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 });
- _eventQueue.Post(DoLocalUpdate);
- }
-
- static ServerStatusWatcher()
- {
- ServerStatusWatcher.Instance = new ServerStatusWatcher();
+ _eventQueue.Post(DoUpdateAsync);
}
public static ServerStatusWatcher Instance
@@ -84,10 +86,134 @@ namespace ServerManagerTool.Lib
return registration;
}
+ private async Task DoUpdateAsync()
+ {
+ try
+ {
+ foreach (var registration in _serverRegistrations)
+ {
+ var statusUpdate = new ServerStatusUpdate();
+
+ try
+ {
+ Logger.Info($"{nameof(DoUpdateAsync)} Start: {registration.LocalEndpoint}, {registration.PublicEndpoint}");
+ statusUpdate = await DoServerStatusUpdateAsync(registration);
+
+ PostServerStatusUpdate(registration, statusUpdate);
+ }
+ catch (Exception ex)
+ {
+ // We don't want to stop other registration queries or break the ActionBlock
+ Logger.Error($"{nameof(DoUpdateAsync)} - Exception in local update. {ex.Message}\r\n{ex.StackTrace}");
+ Debugger.Break();
+ }
+ finally
+ {
+ Logger.Info($"{nameof(DoUpdateAsync)} End: {registration.LocalEndpoint}, {registration.PublicEndpoint}, Status: {statusUpdate.Status}");
+ }
+ }
+ }
+ finally
+ {
+ Task.Delay(Config.Default.ServerStatusWatcher_LocalStatusQueryDelay)
+ .ContinueWith(_ => _eventQueue.Post(DoUpdateAsync))
+ .DoNotWait();
+ }
+ }
+
+ private async Task DoServerStatusUpdateAsync(ServerStatusUpdateRegistration registration)
+ {
+ var registrationKey = registration.PublicEndpoint.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(DoServerStatusUpdateAsync)} Checking server local network status at {registration.LocalEndpoint}");
+
+ // get the server information direct from the server using local connection.
+ var serverStatus = GetLocalNetworkStatus(registration.LocalEndpoint, out ServerInfo localInfo, out int onlinePlayerCount);
+
+ if (serverStatus)
+ {
+ currentStatus = WatcherServerStatus.RunningLocalCheck;
+
+ //
+ // Now that it's running, we can check the publication status.
+ //
+ Logger.Info($"{nameof(DoServerStatusUpdateAsync)} Checking server public (directly) status at {registration.PublicEndpoint}");
+
+ // get the server information direct from the server using public connection.
+ serverStatus = GetPublicNetworkStatusDirectly(registration.PublicEndpoint);
+
+ // 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(DoServerStatusUpdateAsync)} Checking server public (via api) status at {registration.PublicEndpoint}");
+
+ // 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.PublicEndpoint.Address, registration.PublicEndpoint.Port));
+ serverStatus = await GetPublicNetworkStatusViaAPIAsync(uri, registration.PublicEndpoint);
+ }
+
+ _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 ServerProcessStatus GetServerProcessStatus(ServerStatusUpdateRegistration updateContext, out Process serverProcess)
{
serverProcess = null;
- if (String.IsNullOrWhiteSpace(updateContext.InstallDirectory))
+ if (string.IsNullOrWhiteSpace(updateContext.InstallDirectory))
{
return ServerProcessStatus.NotInstalled;
}
@@ -107,19 +233,19 @@ namespace ServerManagerTool.Lib
{
var commandLine = ProcessUtils.GetCommandLineForProcess(process.Id)?.ToLower();
- if (commandLine != null &&
+ if (commandLine != null &&
(commandLine.StartsWith(serverExePath, StringComparison.OrdinalIgnoreCase) || commandLine.StartsWith($"\"{serverExePath}\"", StringComparison.OrdinalIgnoreCase)))
{
// Does this match our server exe and port?
- var serverArgMatch = String.Format(Config.Default.ServerCommandLineArgsMatchFormat, updateContext.LocalEndpoint.Port).ToLower();
+ var serverArgMatch = string.Format(Config.Default.ServerCommandLineArgsMatchFormat, updateContext.LocalEndpoint.Port).ToLower();
if (commandLine.Contains(serverArgMatch))
{
// Was an IP set on it?
- var anyIpArgMatch = String.Format(Config.Default.ServerCommandLineArgsIPMatchFormat, String.Empty).ToLower();
+ 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();
+ var ipArgMatch = string.Format(Config.Default.ServerCommandLineArgsIPMatchFormat, updateContext.LocalEndpoint.Address.ToString()).ToLower();
if (!commandLine.Contains(ipArgMatch))
{
// Specific IP set didn't match
@@ -151,160 +277,20 @@ namespace ServerManagerTool.Lib
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 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 GenerateServerStatusUpdateAsync(ServerStatusUpdateRegistration registration)
- {
- var registrationKey = registration.PublicEndpoint.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.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 (directly) status at {registration.PublicEndpoint}");
-
- // get the server information direct from the server using public connection.
- var serverStatus = CheckServerStatusDirect(registration.PublicEndpoint);
- // 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 (via api) status at {registration.PublicEndpoint}");
-
- // 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.PublicEndpoint.Address, registration.PublicEndpoint.Port));
- serverStatus = await NetworkUtils.CheckServerStatusViaAPI(uri, registration.PublicEndpoint);
- }
-
- _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(IPEndPoint endpoint, out QueryMaster.ServerInfo serverInfo, out int onlinePlayerCount)
+ private static bool GetLocalNetworkStatus(IPEndPoint endpoint, out ServerInfo serverInfo, out int onlinePlayerCount)
{
serverInfo = null;
onlinePlayerCount = 0;
try
{
- using (var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint))
+ using (var server = ServerQuery.GetServerInstance(EngineType.Source, endpoint))
{
try
{
serverInfo = server?.GetInfo();
}
- catch (Exception)
+ catch (Exception)
{
serverInfo = null;
}
@@ -314,28 +300,34 @@ namespace ServerManagerTool.Lib
var playerInfo = server?.GetPlayers()?.Where(p => !string.IsNullOrWhiteSpace(p.Name?.Trim()));
onlinePlayerCount = playerInfo?.Count() ?? 0;
}
- catch (Exception)
+ catch (Exception)
{
onlinePlayerCount = 0;
}
}
+
+ return serverInfo != null;
}
catch (SocketException ex)
{
- Logger.Debug($"{nameof(GetLocalNetworkStatus)} failed: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
// Common when the server is unreachable. Ignore it.
+ Logger.Debug($"{nameof(GetLocalNetworkStatus)} - Failed checking local status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug($"{nameof(GetLocalNetworkStatus)} - Failed checking local status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
}
- return true;
+ return false;
}
- private static bool CheckServerStatusDirect(IPEndPoint endpoint)
+ private static bool GetPublicNetworkStatusDirectly(IPEndPoint endpoint)
{
+ ServerInfo serverInfo;
+
try
{
- QueryMaster.ServerInfo serverInfo;
-
- using (var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint))
+ using (var server = ServerQuery.GetServerInstance(EngineType.Source, endpoint))
{
serverInfo = server.GetInfo();
}
@@ -344,9 +336,69 @@ namespace ServerManagerTool.Lib
}
catch (Exception ex)
{
- Logger.Debug($"{nameof(CheckServerStatusDirect)} - Failed checking status direct for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
- return false;
+ Logger.Debug($"{nameof(GetPublicNetworkStatusDirectly)} - Failed checking public status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
}
+
+ return false;
+ }
+
+ public static async Task GetPublicNetworkStatusViaAPIAsync(Uri uri, IPEndPoint endpoint)
+ {
+ try
+ {
+ string jsonString;
+ using (var client = new WebClient())
+ {
+ jsonString = await client.DownloadStringTaskAsync(uri);
+ }
+
+ if (jsonString == null)
+ {
+ Logger.Debug($"Server info request returned null string for {endpoint.Address}:{endpoint.Port}");
+ return false;
+ }
+
+ JObject query = JObject.Parse(jsonString);
+ if (query == null)
+ {
+ Logger.Debug($"Server info request failed to parse for {endpoint.Address}:{endpoint.Port} - '{jsonString}'");
+ return false;
+ }
+
+ var available = query.SelectToken("available");
+ if (available == null)
+ {
+ Logger.Debug($"Server at {endpoint.Address}:{endpoint.Port} returned no availability.");
+ return false;
+ }
+
+ return (bool)available;
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug($"{nameof(GetPublicNetworkStatusViaAPIAsync)} - Failed checking public status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
+ }
+
+ return false;
+ }
+
+ private void PostServerStatusUpdate(ServerStatusUpdateRegistration registration, ServerStatusUpdate statusUpdate)
+ {
+ _eventQueue.Post(() =>
+ {
+ if (_serverRegistrations.Contains(registration))
+ {
+ try
+ {
+ registration.UpdateCallback(registration, statusUpdate);
+ }
+ catch (Exception ex)
+ {
+ DebugUtils.WriteFormatThreadSafeAsync("Exception during server status update callback: {0}\n{1}", ex.Message, ex.StackTrace).DoNotWait();
+ }
+ }
+ return TaskUtils.FinishedTask;
+ });
}
}
}
diff --git a/src/ARKServerManager/UserControls/ServerSettingsControl.xaml b/src/ARKServerManager/UserControls/ServerSettingsControl.xaml
index 941ea50e..a59642aa 100644
--- a/src/ARKServerManager/UserControls/ServerSettingsControl.xaml
+++ b/src/ARKServerManager/UserControls/ServerSettingsControl.xaml
@@ -425,12 +425,19 @@
+
-
-
+
+
+
+
+
+
+
+
diff --git a/src/ARKServerManager/VersionFeed.xml b/src/ARKServerManager/VersionFeed.xml
index 08a79de1..7d9a8dc5 100644
--- a/src/ARKServerManager/VersionFeed.xml
+++ b/src/ARKServerManager/VersionFeed.xml
@@ -9,8 +9,8 @@
urn:uuid:FF41F7AB-66B0-4504-9F2C-2E1325DB50E2
- 1.1.434 (1.1.434.2)
- 1.1.434.2
+ 1.1.434 (1.1.434.3)
+ 1.1.434.3
2022-06-22T00:00:00Z
@@ -19,9 +19,10 @@
CHANGE
- - 3rd Party Libraries - updated all library to latest versions.
+ - Availability Status - Changed the Waiting for Publication status into two new statuses, to clarify the issue.
- Rules Section - Fjordur Settings - added checkbox to enable/disable settings.
- Rules Section - Ragnarok Settings - added settings for Ragnarok, located at the bottom of the section.
+ - 3rd Party Libraries - updated all library to latest versions.
diff --git a/src/ARKServerManager/VersionFeedBeta.xml b/src/ARKServerManager/VersionFeedBeta.xml
index ead18690..b7a6c12b 100644
--- a/src/ARKServerManager/VersionFeedBeta.xml
+++ b/src/ARKServerManager/VersionFeedBeta.xml
@@ -7,6 +7,29 @@
2022-06-22T00:00:00Z
+
+ urn:uuid:F3965FC7-685F-47DF-9E5B-3BA4D928127F
+ 1.1.434 (1.1.434.3)
+ 1.1.434.2
+
+ 2022-06-22T00:00:00Z
+
+
+
+ CHANGE
+
+
+ - Availability Status - Changed the Waiting for Publication status into two new statuses, to clarify the issue.
+
+
+
+
+
+ bletch
+ bletch1971@hotmail.com
+
+
+
urn:uuid:F3965FC7-685F-47DF-9E5B-3BA4D928127F
1.1.434 (1.1.434.2)
diff --git a/src/ConanServerManager/App.config b/src/ConanServerManager/App.config
index 05526835..939747ba 100644
--- a/src/ConanServerManager/App.config
+++ b/src/ConanServerManager/App.config
@@ -182,7 +182,7 @@
-MultiHome={0}
-
+
-QueryPort={0}
diff --git a/src/ConanServerManager/App.xaml.cs b/src/ConanServerManager/App.xaml.cs
index 394f166f..83874b10 100644
--- a/src/ConanServerManager/App.xaml.cs
+++ b/src/ConanServerManager/App.xaml.cs
@@ -672,12 +672,12 @@ namespace ServerManagerTool
if (Config.Default.LoggingEnabled)
{
while (!LogManager.IsLoggingEnabled())
- LogManager.EnableLogging();
+ LogManager.ResumeLogging();
}
else
{
while (LogManager.IsLoggingEnabled())
- LogManager.DisableLogging();
+ LogManager.SuspendLogging();
}
Debug.WriteLine($"Logging Enabled: {LogManager.IsLoggingEnabled()}");
diff --git a/src/ConanServerManager/Config.Designer.cs b/src/ConanServerManager/Config.Designer.cs
index be0cbdd2..36f47b63 100644
--- a/src/ConanServerManager/Config.Designer.cs
+++ b/src/ConanServerManager/Config.Designer.cs
@@ -1370,9 +1370,9 @@ namespace ServerManagerTool {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("-QueryPort={0}")]
- public string ServerCommandLineArgsPortMatchFormat {
+ public string ServerCommandLineArgsMatchFormat {
get {
- return ((string)(this["ServerCommandLineArgsPortMatchFormat"]));
+ return ((string)(this["ServerCommandLineArgsMatchFormat"]));
}
}
diff --git a/src/ConanServerManager/Config.settings b/src/ConanServerManager/Config.settings
index ff8c2d3f..19adc3af 100644
--- a/src/ConanServerManager/Config.settings
+++ b/src/ConanServerManager/Config.settings
@@ -377,7 +377,7 @@
-MultiHome={0}
-
+
-QueryPort={0}
diff --git a/src/ConanServerManager/Globalization/en-US/en-US.xaml b/src/ConanServerManager/Globalization/en-US/en-US.xaml
index 92c344d7..cdc2e3ea 100644
--- a/src/ConanServerManager/Globalization/en-US/en-US.xaml
+++ b/src/ConanServerManager/Globalization/en-US/en-US.xaml
@@ -1012,14 +1012,19 @@
Last Start Time:
Availability:
The status of the server on Steam.
- Unknown
- Set Public IP
- Unavailable
- Waiting for publication
- Available
Players:
The current number of players connected to the server.
+ Unknown
+ Unavailable
+ Server in not accessible
+ LAN Only
+ Server accessible via Local IP only. Port Forwarding, Firewall or CGNAT etc are preventing public access.
+ Available - No Loopback
+ Server accessible via Local IP and Public IP, loopback not available. Server is public but Public IP not accessible via LAN.
+ Available
+ Server accessible via Local IP and Public IP
+
Initializing
Running
Stopped
diff --git a/src/ConanServerManager/Lib/ServerProfile.cs b/src/ConanServerManager/Lib/ServerProfile.cs
index 885d9a35..f3c135e2 100644
--- a/src/ConanServerManager/Lib/ServerProfile.cs
+++ b/src/ConanServerManager/Lib/ServerProfile.cs
@@ -875,7 +875,7 @@ namespace ServerManagerTool.Lib
}
serverArgs.Append($" -Port={this.ServerPort}");
serverArgs.Append(" ");
- serverArgs.AppendFormat(Config.Default.ServerCommandLineArgsPortMatchFormat, this.QueryPort);
+ serverArgs.AppendFormat(Config.Default.ServerCommandLineArgsMatchFormat, this.QueryPort);
if (this.RconEnabled)
{
serverArgs.AppendFormat($" -RconPort={this.RconPort}");
diff --git a/src/ConanServerManager/Lib/ServerRuntime.cs b/src/ConanServerManager/Lib/ServerRuntime.cs
index cd617dd6..930a4f4a 100644
--- a/src/ConanServerManager/Lib/ServerRuntime.cs
+++ b/src/ConanServerManager/Lib/ServerRuntime.cs
@@ -193,7 +193,7 @@ namespace ServerManagerTool.Lib
//
// Get the local endpoint for querying the local network
//
- if (!ushort.TryParse(this.ProfileSnapshot.QueryPort.ToString(), out ushort port))
+ if (!ushort.TryParse(this.ProfileSnapshot.QueryPort.ToString(), out _))
{
_logger.Error($"Port is out of range ({this.ProfileSnapshot.QueryPort})");
return;
@@ -205,7 +205,7 @@ namespace ServerManagerTool.Lib
// Get the public endpoint for querying Steam
//
steamServerQueryEndPoint = null;
- if (!String.IsNullOrWhiteSpace(Config.Default.MachinePublicIP))
+ if (!string.IsNullOrWhiteSpace(Config.Default.MachinePublicIP))
{
if (IPAddress.TryParse(Config.Default.MachinePublicIP, out IPAddress steamServerIpAddress))
{
@@ -237,7 +237,7 @@ namespace ServerManagerTool.Lib
private void ProcessStatusUpdate(IAsyncDisposable registration, ServerStatusUpdate update)
{
- if(!Object.ReferenceEquals(registration, this.updateRegistration))
+ if (!ReferenceEquals(registration, this.updateRegistration))
{
return;
}
@@ -245,64 +245,90 @@ namespace ServerManagerTool.Lib
TaskUtils.RunOnUIThreadAsync(() =>
{
var oldStatus = this.Status;
+ var oldAvailability = this.Availability;
+
switch (update.Status)
{
+ case WatcherServerStatus.Unknown:
+ if (oldStatus != ServerStatus.Updating)
+ UpdateServerStatus(ServerStatus.Unknown, AvailabilityStatus.Unknown, false);
+
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ break;
+
case WatcherServerStatus.NotInstalled:
if (oldStatus != ServerStatus.Updating)
UpdateServerStatus(ServerStatus.Uninstalled, AvailabilityStatus.Unavailable, false);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
- break;
- case WatcherServerStatus.Initializing:
- if (oldStatus != ServerStatus.Stopping)
- UpdateServerStatus(ServerStatus.Initializing, AvailabilityStatus.Unavailable, oldStatus != ServerStatus.Initializing && oldStatus != ServerStatus.Unknown);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
case WatcherServerStatus.Stopped:
if (oldStatus != ServerStatus.Updating)
UpdateServerStatus(ServerStatus.Stopped, AvailabilityStatus.Unavailable, oldStatus == ServerStatus.Initializing || oldStatus == ServerStatus.Running || oldStatus == ServerStatus.Stopping);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
- case WatcherServerStatus.Unknown:
- if (oldStatus != ServerStatus.Updating)
- UpdateServerStatus(ServerStatus.Unknown, AvailabilityStatus.Unknown, false);
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ case WatcherServerStatus.Initializing:
+ if (oldStatus != ServerStatus.Stopping)
+ UpdateServerStatus(ServerStatus.Initializing, AvailabilityStatus.Unavailable, oldStatus != ServerStatus.Initializing && oldStatus != ServerStatus.Unknown);
+
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
case WatcherServerStatus.RunningLocalCheck:
if (oldStatus != ServerStatus.Stopping)
{
- UpdateServerStatus(ServerStatus.Running, this.Availability != AvailabilityStatus.Available ? AvailabilityStatus.Waiting : this.Availability, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
- if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Start();
+ UpdateServerStatus(ServerStatus.Running, oldAvailability != AvailabilityStatus.Available ? AvailabilityStatus.LocalOnly : AvailabilityStatus.Available, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
+
+ if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Start();
}
else
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ {
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ }
break;
case WatcherServerStatus.RunningExternalCheck:
if (oldStatus != ServerStatus.Stopping)
{
- UpdateServerStatus(ServerStatus.Running, AvailabilityStatus.Waiting, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
- if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Start();
+ UpdateServerStatus(ServerStatus.Running, AvailabilityStatus.PublicOnly, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
+
+ if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Start();
}
else
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ {
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ }
break;
case WatcherServerStatus.Published:
if (oldStatus != ServerStatus.Stopping)
{
UpdateServerStatus(ServerStatus.Running, AvailabilityStatus.Available, oldStatus != ServerStatus.Running && oldStatus != ServerStatus.Unknown);
- if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Start();
+
+ if (this.ProfileSnapshot.MOTDIntervalEnabled && this.motdIntervalTimer != null && !this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Start();
}
else
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ {
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
+ }
break;
default:
- if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled) this.motdIntervalTimer.Stop();
+ if (this.motdIntervalTimer != null && this.motdIntervalTimer.Enabled)
+ this.motdIntervalTimer.Stop();
break;
}
@@ -322,7 +348,7 @@ namespace ServerManagerTool.Lib
if (match.Success && match.Groups.Count >= 2)
{
var serverVersion = $"{version}.{match.Groups[1].Value}";
- if (!String.IsNullOrWhiteSpace(serverVersion) && Version.TryParse(serverVersion, out Version temp))
+ if (!string.IsNullOrWhiteSpace(serverVersion) && Version.TryParse(serverVersion, out Version temp))
{
this.Version = temp;
}
@@ -385,7 +411,7 @@ namespace ServerManagerTool.Lib
if (localServerQueryEndPoint == null || steamServerQueryEndPoint == null)
return;
- this.updateRegistration = ServerStatusWatcher.Instance.RegisterForUpdates(this.ProfileSnapshot.InstallDirectory, this.ProfileSnapshot.ProfileId, this.ProfileSnapshot.GameFile, localServerQueryEndPoint, steamServerQueryEndPoint, ProcessStatusUpdate);
+ this.updateRegistration = ServerStatusWatcher.Instance.RegisterForUpdates(this.ProfileSnapshot.InstallDirectory, this.ProfileSnapshot.ProfileId, localServerQueryEndPoint, steamServerQueryEndPoint, ProcessStatusUpdate);
}
}
diff --git a/src/ConanServerManager/Lib/ServerStatusWatcher.cs b/src/ConanServerManager/Lib/ServerStatusWatcher.cs
index 068422f9..20d3ba91 100644
--- a/src/ConanServerManager/Lib/ServerStatusWatcher.cs
+++ b/src/ConanServerManager/Lib/ServerStatusWatcher.cs
@@ -1,4 +1,6 @@
-using NLog;
+using Newtonsoft.Json.Linq;
+using NLog;
+using QueryMaster;
using ServerManagerTool.Common.Enums;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
@@ -30,7 +32,7 @@ namespace ServerManagerTool.Lib
private ServerStatusWatcher()
{
_eventQueue = new ActionBlock>(async f => await f.Invoke(), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 });
- _eventQueue.Post(DoLocalUpdate);
+ _eventQueue.Post(DoUpdateAsync);
}
public static ServerStatusWatcher Instance
@@ -39,7 +41,7 @@ namespace ServerManagerTool.Lib
private set;
}
- public IAsyncDisposable RegisterForUpdates(string installDirectory, string profileId, string gameFile, IPEndPoint localEndpoint, IPEndPoint steamEndpoint, Action updateCallback)
+ public IAsyncDisposable RegisterForUpdates(string installDirectory, string profileId, IPEndPoint localEndpoint, IPEndPoint steamEndpoint, Action updateCallback)
{
var registration = new ServerStatusUpdateRegistration
{
@@ -84,125 +86,42 @@ namespace ServerManagerTool.Lib
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.StartsWith(serverExePath, StringComparison.OrdinalIgnoreCase) || commandLine.StartsWith($"\"{serverExePath}\"", StringComparison.OrdinalIgnoreCase)))
- {
- // 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()
+ private async Task DoUpdateAsync()
{
try
{
- foreach (var registration in this._serverRegistrations)
+ foreach (var registration in _serverRegistrations)
{
- ServerStatusUpdate statusUpdate = new ServerStatusUpdate();
+ var statusUpdate = new ServerStatusUpdate();
+
try
{
- Logger.Info($"{nameof(DoLocalUpdate)} Start: {registration.LocalEndpoint}");
- statusUpdate = await GenerateServerStatusUpdateAsync(registration);
+ Logger.Info($"{nameof(DoUpdateAsync)} Start: {registration.LocalEndpoint}, {registration.PublicEndpoint}");
+ statusUpdate = await DoServerStatusUpdateAsync(registration);
- PostServerStatusUpdate(registration, registration.UpdateCallback, statusUpdate);
+ PostServerStatusUpdate(registration, 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}");
+ Logger.Error($"{nameof(DoUpdateAsync)} - Exception in local update. {ex.Message}\r\n{ex.StackTrace}");
Debugger.Break();
}
finally
{
- Logger.Info($"{nameof(DoLocalUpdate)} End: {registration.LocalEndpoint}: {statusUpdate.Status}");
+ Logger.Info($"{nameof(DoUpdateAsync)} End: {registration.LocalEndpoint}, {registration.PublicEndpoint}, Status: {statusUpdate.Status}");
}
}
}
finally
{
- Task.Delay(Config.Default.ServerStatusWatcher_LocalStatusQueryDelay).ContinueWith(_ => _eventQueue.Post(DoLocalUpdate)).DoNotWait();
+ Task.Delay(Config.Default.ServerStatusWatcher_LocalStatusQueryDelay)
+ .ContinueWith(_ => _eventQueue.Post(DoUpdateAsync))
+ .DoNotWait();
}
}
- private void PostServerStatusUpdate(ServerStatusUpdateRegistration registration, Action 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 GenerateServerStatusUpdateAsync(ServerStatusUpdateRegistration registration)
+ private async Task DoServerStatusUpdateAsync(ServerStatusUpdateRegistration registration)
{
var registrationKey = registration.PublicEndpoint.ToString();
@@ -234,22 +153,23 @@ namespace ServerManagerTool.Lib
//
// If the process was running do we then perform network checks.
//
- Logger.Info($"{nameof(GenerateServerStatusUpdateAsync)} Checking server local network status at {registration.LocalEndpoint}");
+ Logger.Info($"{nameof(DoServerStatusUpdateAsync)} Checking server local network status at {registration.LocalEndpoint}");
// get the server information direct from the server using local connection.
- GetLocalNetworkStatus(registration.LocalEndpoint, out QueryMaster.ServerInfo localInfo, out int onlinePlayerCount);
+ var serverStatus = GetLocalNetworkStatus(registration.LocalEndpoint, out ServerInfo localInfo, out int onlinePlayerCount);
- if (localInfo != null)
+ if (serverStatus)
{
currentStatus = WatcherServerStatus.RunningLocalCheck;
//
// Now that it's running, we can check the publication status.
//
- Logger.Info($"{nameof(GenerateServerStatusUpdateAsync)} Checking server public (directly) status at {registration.PublicEndpoint}");
+ Logger.Info($"{nameof(DoServerStatusUpdateAsync)} Checking server public (directly) status at {registration.PublicEndpoint}");
// get the server information direct from the server using public connection.
- var serverStatus = CheckServerStatusDirect(registration.PublicEndpoint);
+ serverStatus = GetPublicNetworkStatusDirectly(registration.PublicEndpoint);
+
// check if the server returned the information.
if (!serverStatus)
{
@@ -261,11 +181,11 @@ namespace ServerManagerTool.Lib
if (!string.IsNullOrWhiteSpace(Config.Default.ServerStatusUrlFormat))
{
- Logger.Info($"{nameof(GenerateServerStatusUpdateAsync)} Checking server public (via api) status at {registration.PublicEndpoint}");
+ Logger.Info($"{nameof(DoServerStatusUpdateAsync)} Checking server public (via api) status at {registration.PublicEndpoint}");
// 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.PublicEndpoint.Address, registration.PublicEndpoint.Port));
- serverStatus = await NetworkUtils.CheckServerStatusViaAPI(uri, registration.PublicEndpoint);
+ serverStatus = await GetPublicNetworkStatusViaAPIAsync(uri, registration.PublicEndpoint);
}
_nextExternalStatusQuery[registrationKey] = DateTime.Now.AddMilliseconds(Config.Default.ServerStatusWatcher_RemoteStatusQueryDelay);
@@ -290,18 +210,85 @@ namespace ServerManagerTool.Lib
return await Task.FromResult(statusUpdate);
}
- private static bool GetLocalNetworkStatus(IPEndPoint endpoint, out QueryMaster.ServerInfo serverInfo, out int onlinePlayerCount)
+ 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.StartsWith(serverExePath, StringComparison.OrdinalIgnoreCase) || commandLine.StartsWith($"\"{serverExePath}\"", StringComparison.OrdinalIgnoreCase)))
+ {
+ // Does this match our server exe and port?
+ var serverArgMatch = string.Format(Config.Default.ServerCommandLineArgsMatchFormat, 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 static bool GetLocalNetworkStatus(IPEndPoint endpoint, out ServerInfo serverInfo, out int onlinePlayerCount)
{
serverInfo = null;
onlinePlayerCount = 0;
try
{
- using (var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint))
+ using (var server = ServerQuery.GetServerInstance(EngineType.Source, endpoint))
{
try
{
- serverInfo = server.GetInfo();
+ serverInfo = server?.GetInfo();
}
catch (Exception)
{
@@ -318,23 +305,29 @@ namespace ServerManagerTool.Lib
onlinePlayerCount = 0;
}
}
+
+ return serverInfo != null;
}
catch (SocketException ex)
{
- // Common when the server is unreachable. Log and Ignore it.
- Logger.Debug($"{nameof(GetLocalNetworkStatus)} failed: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
+ // Common when the server is unreachable. Ignore it.
+ Logger.Debug($"{nameof(GetLocalNetworkStatus)} - Failed checking local status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug($"{nameof(GetLocalNetworkStatus)} - Failed checking local status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
}
- return true;
+ return false;
}
- private static bool CheckServerStatusDirect(IPEndPoint endpoint)
+ private static bool GetPublicNetworkStatusDirectly(IPEndPoint endpoint)
{
+ ServerInfo serverInfo;
+
try
{
- QueryMaster.ServerInfo serverInfo;
-
- using (var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint))
+ using (var server = ServerQuery.GetServerInstance(EngineType.Source, endpoint))
{
serverInfo = server.GetInfo();
}
@@ -343,9 +336,69 @@ namespace ServerManagerTool.Lib
}
catch (Exception ex)
{
- Logger.Debug($"{nameof(CheckServerStatusDirect)} - Failed checking status direct for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
- return false;
+ Logger.Debug($"{nameof(GetPublicNetworkStatusDirectly)} - Failed checking public status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
}
+
+ return false;
+ }
+
+ public static async Task GetPublicNetworkStatusViaAPIAsync(Uri uri, IPEndPoint endpoint)
+ {
+ try
+ {
+ string jsonString;
+ using (var client = new WebClient())
+ {
+ jsonString = await client.DownloadStringTaskAsync(uri);
+ }
+
+ if (jsonString == null)
+ {
+ Logger.Debug($"Server info request returned null string for {endpoint.Address}:{endpoint.Port}");
+ return false;
+ }
+
+ JObject query = JObject.Parse(jsonString);
+ if (query == null)
+ {
+ Logger.Debug($"Server info request failed to parse for {endpoint.Address}:{endpoint.Port} - '{jsonString}'");
+ return false;
+ }
+
+ var available = query.SelectToken("available");
+ if (available == null)
+ {
+ Logger.Debug($"Server at {endpoint.Address}:{endpoint.Port} returned no availability.");
+ return false;
+ }
+
+ return (bool)available;
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug($"{nameof(GetPublicNetworkStatusViaAPIAsync)} - Failed checking public status for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
+ }
+
+ return false;
+ }
+
+ private void PostServerStatusUpdate(ServerStatusUpdateRegistration registration, ServerStatusUpdate statusUpdate)
+ {
+ _eventQueue.Post(() =>
+ {
+ if (_serverRegistrations.Contains(registration))
+ {
+ try
+ {
+ registration.UpdateCallback(registration, statusUpdate);
+ }
+ catch (Exception ex)
+ {
+ DebugUtils.WriteFormatThreadSafeAsync("Exception during server status update callback: {0}\n{1}", ex.Message, ex.StackTrace).DoNotWait();
+ }
+ }
+ return TaskUtils.FinishedTask;
+ });
}
}
}
diff --git a/src/ConanServerManager/UserControls/ServerSettingsControl.xaml b/src/ConanServerManager/UserControls/ServerSettingsControl.xaml
index eca25e30..2f500492 100644
--- a/src/ConanServerManager/UserControls/ServerSettingsControl.xaml
+++ b/src/ConanServerManager/UserControls/ServerSettingsControl.xaml
@@ -285,12 +285,19 @@
+
-
-
+
+
+
+
+
+
+
+
diff --git a/src/ConanServerManager/VersionFeed.xml b/src/ConanServerManager/VersionFeed.xml
index 30ec3ca8..996d88d8 100644
--- a/src/ConanServerManager/VersionFeed.xml
+++ b/src/ConanServerManager/VersionFeed.xml
@@ -9,8 +9,8 @@
urn:uuid:CDD1853D-66EA-4649-AD24-E491D64C853E
- 1.1.77 (1.1.77.1)
- 1.1.77.1
+ 1.1.77 (1.1.77.2)
+ 1.1.77.2
2022-06-22T00:00:00Z
@@ -19,6 +19,7 @@
CHANGE
+ - Availability Status - Changed the Waiting for Publication status into two new statuses, to clarify the issue.
- 3rd Party Libraries - updated all library to latest versions.
diff --git a/src/ConanServerManager/VersionFeedBeta.xml b/src/ConanServerManager/VersionFeedBeta.xml
index 7d920fbd..f37e813e 100644
--- a/src/ConanServerManager/VersionFeedBeta.xml
+++ b/src/ConanServerManager/VersionFeedBeta.xml
@@ -7,6 +7,29 @@
2022-06-22T00:00:00Z
+
+ urn:uuid:CDD1853D-66EA-4649-AD24-E491D64C853E
+ 1.1.77 (1.1.77.2)
+ 1.1.77.2
+
+ 2022-06-22T00:00:00Z
+
+
+
+ CHANGE
+
+
+ - Availability Status - Changed the Waiting for Publication status into two new statuses, to clarify the issue.
+
+
+
+
+
+ bletch
+ bletch1971@hotmail.com
+
+
+
urn:uuid:CDD1853D-66EA-4649-AD24-E491D64C853E
1.1.77 (1.1.77.1)
diff --git a/src/ServerManager.Common/Enums/AvailabilityStatus.cs b/src/ServerManager.Common/Enums/AvailabilityStatus.cs
index c5747e99..ec7b3695 100644
--- a/src/ServerManager.Common/Enums/AvailabilityStatus.cs
+++ b/src/ServerManager.Common/Enums/AvailabilityStatus.cs
@@ -4,7 +4,8 @@
{
Unknown,
Unavailable,
- Waiting,
- Available
+ LocalOnly,
+ PublicOnly,
+ Available,
}
}
diff --git a/src/ServerManager.Common/Utils/NetworkUtils.cs b/src/ServerManager.Common/Utils/NetworkUtils.cs
index 5183865b..4b2603b9 100644
--- a/src/ServerManager.Common/Utils/NetworkUtils.cs
+++ b/src/ServerManager.Common/Utils/NetworkUtils.cs
@@ -1,5 +1,4 @@
-using Newtonsoft.Json.Linq;
-using NLog;
+using NLog;
using ServerManagerTool.Common.Lib;
using System;
using System.Collections.Generic;
@@ -129,45 +128,6 @@ namespace ServerManagerTool.Common.Utils
}
}
- public static async Task CheckServerStatusViaAPI(Uri uri, IPEndPoint endpoint)
- {
- try
- {
- string jsonString;
- using (var client = new WebClient())
- {
- jsonString = await client.DownloadStringTaskAsync(uri);
- }
-
- if (jsonString == null)
- {
- _logger.Debug($"Server info request returned null string for {endpoint.Address}:{endpoint.Port}");
- return false;
- }
-
- JObject query = JObject.Parse(jsonString);
- if (query == null)
- {
- _logger.Debug($"Server info request failed to parse for {endpoint.Address}:{endpoint.Port} - '{jsonString}'");
- return false;
- }
-
- var available = query.SelectToken("available");
- if (available == null)
- {
- _logger.Debug($"Server at {endpoint.Address}:{endpoint.Port} returned no availability.");
- return false;
- }
-
- return (bool)available;
- }
- catch (Exception ex)
- {
- _logger.Debug($"{nameof(CheckServerStatusViaAPI)} - Failed checking status via API for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
- return false;
- }
- }
-
public static async Task PerformCallToAPIAsync(Uri uri)
{
try