diff --git a/src/ARKServerManager/Globalization/en-US/en-US.xaml b/src/ARKServerManager/Globalization/en-US/en-US.xaml
index ff7555cc..52a3edc3 100644
--- a/src/ARKServerManager/Globalization/en-US/en-US.xaml
+++ b/src/ARKServerManager/Globalization/en-US/en-US.xaml
@@ -2607,8 +2607,8 @@
DisconnectedConnected
- Player '{0}' joined the game.
- Player '{0}' left the game.
+ '{0}' joined the game.
+ '{0}' left the game.
diff --git a/src/ARKServerManager/Lib/ServerRCON.cs b/src/ARKServerManager/Lib/ServerRCON.cs
index 4f498db2..3f2a0b78 100644
--- a/src/ARKServerManager/Lib/ServerRCON.cs
+++ b/src/ARKServerManager/Lib/ServerRCON.cs
@@ -1,5 +1,6 @@
using ArkData;
using NLog;
+using ServerManagerTool.Common.Enums;
using ServerManagerTool.Common.Extensions;
using ServerManagerTool.Common.Interfaces;
using ServerManagerTool.Common.Lib;
@@ -24,8 +25,6 @@ namespace ServerManagerTool.Lib
{
public class ServerRCON : DependencyObject, IAsyncDisposable
{
- public event EventHandler PlayersCollectionUpdated;
-
private const int STEAM_UPDATE_INTERVAL = 60;
private const int PLAYER_LIST_INTERVAL = 5000;
private const int GET_CHAT_INTERVAL = 1000;
@@ -38,36 +37,7 @@ namespace ServerManagerTool.Lib
public const string RCON_COMMAND_SERVERCHAT = "serverchat";
public const string RCON_COMMAND_WILDDINOWIPE = "DestroyWildDinos";
- [TypeConverter(typeof(EnumDescriptionTypeConverter))]
- public enum ConsoleStatus
- {
- Disconnected,
- Connected,
- };
-
- public class ConsoleCommand
- {
- public ConsoleStatus status;
- public string rawCommand;
-
- public string command;
- public string args;
-
- public bool suppressCommand;
- public bool suppressOutput;
- public IEnumerable lines = new string[0];
- };
-
- private class CommandListener : IDisposable
- {
- public Action Callback { get; set; }
- public Action DisposeAction { get; set; }
-
- public void Dispose()
- {
- DisposeAction(this);
- }
- }
+ public event EventHandler PlayersCollectionUpdated;
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(nameof(Status), typeof(ConsoleStatus), typeof(ServerRCON), new PropertyMetadata(ConsoleStatus.Disconnected));
public static readonly DependencyProperty PlayersProperty = DependencyProperty.Register(nameof(Players), typeof(SortableObservableCollection), typeof(ServerRCON), new PropertyMetadata(null));
@@ -77,16 +47,18 @@ namespace ServerManagerTool.Lib
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 commandListeners = new List();
- private readonly RCONParameters rconParams;
- private QueryMaster.Rcon console;
- private int maxCommandRetries = 3;
- private readonly ConcurrentDictionary players = new ConcurrentDictionary();
- private readonly object updatePlayerCollectionLock = new object();
- private CancellationTokenSource cancellationTokenSource = null;
+ private readonly ActionQueue _commandProcessor = new ActionQueue(TaskScheduler.Default);
+ private readonly ActionQueue _outputProcessor = new ActionQueue(TaskScheduler.FromCurrentSynchronizationContext());
+ private readonly List _commandListeners = new List();
+
+ private readonly RCONParameters _rconParameters;
+ private QueryMaster.Rcon _console;
+ private int _maxCommandRetries = 3;
+
+ private readonly ConcurrentDictionary _players = new ConcurrentDictionary();
+ private readonly object _updatePlayerCollectionLock = new object();
+ private CancellationTokenSource _cancellationTokenSource = null;
private readonly Logger _chatLogger;
private readonly Logger _allLogger;
@@ -97,28 +69,30 @@ namespace ServerManagerTool.Lib
public ServerRCON(RCONParameters parameters)
{
- this.rconParams = parameters;
- this.Players = new SortableObservableCollection();
+ _rconParameters = parameters;
- _allLogger = App.GetProfileLogger(this.rconParams.ProfileId, "RCON_All", LogLevel.Info, LogLevel.Info);
- _chatLogger = App.GetProfileLogger(this.rconParams.ProfileId, "RCON_Chat", LogLevel.Info, LogLevel.Info);
- _eventLogger = App.GetProfileLogger(this.rconParams.ProfileId, "RCON_Event", LogLevel.Info, LogLevel.Info);
- _debugLogger = App.GetProfileLogger(this.rconParams.ProfileId, "RCON_Debug", LogLevel.Trace, LogLevel.Debug);
- _errorLogger = App.GetProfileLogger(this.rconParams.ProfileId, "RCON_Error", LogLevel.Error, LogLevel.Fatal);
+ _allLogger = App.GetProfileLogger(_rconParameters.ProfileId, "RCON_All", LogLevel.Info, LogLevel.Info);
+ _chatLogger = App.GetProfileLogger(_rconParameters.ProfileId, "RCON_Chat", LogLevel.Info, LogLevel.Info);
+ _eventLogger = App.GetProfileLogger(_rconParameters.ProfileId, "RCON_Event", LogLevel.Info, LogLevel.Info);
+ _debugLogger = App.GetProfileLogger(_rconParameters.ProfileId, "RCON_Debug", LogLevel.Trace, LogLevel.Debug);
+ _errorLogger = App.GetProfileLogger(_rconParameters.ProfileId, "RCON_Error", LogLevel.Error, LogLevel.Fatal);
+
+ this.Players = new SortableObservableCollection();
}
public async Task DisposeAsync()
{
- if (cancellationTokenSource != null)
+ if (_cancellationTokenSource != null)
{
- cancellationTokenSource.Cancel();
+ _cancellationTokenSource.Cancel();
}
- await this.commandProcessor.DisposeAsync();
- await this.outputProcessor.DisposeAsync();
- for (int index = this.commandListeners.Count - 1; index >= 0; index--)
+ await _commandProcessor.DisposeAsync();
+ await _outputProcessor.DisposeAsync();
+
+ for (int index = _commandListeners.Count - 1; index >= 0; index--)
{
- this.commandListeners[index].Dispose();
+ _commandListeners[index].Dispose();
}
_disposed = true;
@@ -159,8 +133,8 @@ namespace ServerManagerTool.Lib
#region Methods
public void Initialize()
{
- commandProcessor.PostAction(AutoPlayerList);
- commandProcessor.PostAction(AutoGetChat);
+ _commandProcessor.PostAction(AutoPlayerList);
+ _commandProcessor.PostAction(AutoGetChat);
UpdatePlayersAsync().DoNotWait();
}
@@ -189,50 +163,55 @@ namespace ServerManagerTool.Lib
private bool Reconnect()
{
- if (this.console != null)
+ if (_console != null)
{
- this.console.Dispose();
- this.console = null;
+ _console.Dispose();
+ _console = null;
}
- var endpoint = new IPEndPoint(this.rconParams.RCONHostIP, this.rconParams.RCONPort);
+ var endpoint = new IPEndPoint(_rconParameters.RCONHostIP, _rconParameters.RCONPort);
var server = QueryMaster.ServerQuery.GetServerInstance(QueryMaster.EngineType.Source, endpoint);
- this.console = server.GetControl(this.rconParams.RCONPassword);
- return this.console != null;
+ _console = server.GetControl(_rconParameters.RCONPassword);
+ return _console != null;
}
public IDisposable RegisterCommandListener(Action callback)
{
var listener = new CommandListener { Callback = callback, DisposeAction = UnregisterCommandListener };
- this.commandListeners.Add(listener);
+ _commandListeners.Add(listener);
return listener;
}
private void UnregisterCommandListener(CommandListener listener)
{
- this.commandListeners.Remove(listener);
+ _commandListeners.Remove(listener);
}
#endregion
#region Process Methods
private Task AutoPlayerList()
{
- return commandProcessor.PostAction(() =>
+ return _commandProcessor.PostAction(() =>
{
ProcessInput(new ConsoleCommand() { rawCommand = RCON_COMMAND_LISTPLAYERS, suppressCommand = true, suppressOutput = true });
- Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t => commandProcessor.PostAction(AutoPlayerList)).DoNotWait();
+ Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t => _commandProcessor.PostAction(AutoPlayerList)).DoNotWait();
});
}
private Task AutoGetChat()
{
- return commandProcessor.PostAction(() =>
+ return _commandProcessor.PostAction(() =>
{
ProcessInput(new ConsoleCommand() { rawCommand = RCON_COMMAND_GETCHAT, suppressCommand = true, suppressOutput = true });
- Task.Delay(GET_CHAT_INTERVAL).ContinueWith(t => commandProcessor.PostAction(AutoGetChat)).DoNotWait();
+ Task.Delay(GET_CHAT_INTERVAL).ContinueWith(t => _commandProcessor.PostAction(AutoGetChat)).DoNotWait();
});
}
+ public Task IssueCommand(string userCommand)
+ {
+ return _commandProcessor.PostAction(() => ProcessInput(new ConsoleCommand() { rawCommand = userCommand }));
+ }
+
private bool ProcessInput(ConsoleCommand command)
{
try
@@ -277,14 +256,14 @@ namespace ServerManagerTool.Lib
command.status = ConsoleStatus.Connected;
- this.outputProcessor.PostAction(() => ProcessOutput(command));
+ _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));
+ _outputProcessor.PostAction(() => ProcessOutput(command));
return false;
}
}
@@ -299,11 +278,6 @@ namespace ServerManagerTool.Lib
NotifyCommand(command);
}
- public Task IssueCommand(string userCommand)
- {
- return this.commandProcessor.PostAction(() => ProcessInput(new ConsoleCommand() { rawCommand = userCommand }));
- }
-
// This is bound to the UI thread
private void HandleCommand(ConsoleCommand command)
{
@@ -320,8 +294,8 @@ namespace ServerManagerTool.Lib
//
// Update the visible player list
//
- command.suppressOutput = false;
command.lines = HandleListPlayersCommand(command.lines);
+ command.suppressOutput = command.lines.Count() == 0;
}
if (command.command.Equals(RCON_COMMAND_GETCHAT, StringComparison.OrdinalIgnoreCase))
@@ -361,11 +335,13 @@ namespace ServerManagerTool.Lib
private List HandleListPlayersCommand(IEnumerable commandLines)
{
var output = new List();
+ var onlinePlayers = new List();
- if (commandLines != null)
+ var playerLines = commandLines?.ToList() ?? new List();
+ if (playerLines.Count > 0)
{
- var onlinePlayers = new List();
- foreach (var line in commandLines)
+
+ foreach (var line in playerLines)
{
var elements = line.Split(',');
if (elements.Length != 2)
@@ -373,9 +349,6 @@ namespace ServerManagerTool.Lib
continue;
var id = elements[1]?.Trim();
- if (id.Any(c => !char.IsDigit(c)))
- // Invalid data. Ignore it.
- continue;
if (onlinePlayers.FirstOrDefault(p => p.PlayerId.Equals(id, StringComparison.OrdinalIgnoreCase)) != null)
// Duplicate data. Ignore it.
@@ -390,43 +363,56 @@ namespace ServerManagerTool.Lib
onlinePlayers.Add(newPlayer);
var playerJoined = false;
- this.players.AddOrUpdate(newPlayer.PlayerId, (k) => { playerJoined = true; return newPlayer; }, (k, v) => { playerJoined = !v.IsOnline; v.IsOnline = true; return v; });
+ _players.AddOrUpdate(newPlayer.PlayerId,
+ (k) =>
+ {
+ playerJoined = true;
+ return newPlayer;
+ },
+ (k, v) =>
+ {
+ playerJoined = !v.IsOnline;
+ v.PlayerName = newPlayer.PlayerName;
+ v.IsOnline = newPlayer.IsOnline;
+ return v;
+ }
+ );
if (playerJoined)
{
- var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Join") ?? "Player '{0}' joined the game.";
+ var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Join") ?? "'{0}' joined the game.";
var message = string.Format(messageFormat, newPlayer.PlayerName);
output.Add(message);
LogEvent(LogEventType.Event, message);
LogEvent(LogEventType.All, message);
}
}
-
- var droppedPlayers = this.players.Values.Where(p => onlinePlayers.FirstOrDefault(np => np.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null);
- foreach (var droppedPlayer in droppedPlayers)
- {
- if (droppedPlayer.IsOnline)
- {
- droppedPlayer.IsOnline = false;
-
- var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Leave") ?? "Player '{0}' left the game.";
- var message = string.Format(messageFormat, droppedPlayer.PlayerName);
- output.Add(message);
- LogEvent(LogEventType.Event, message);
- LogEvent(LogEventType.All, message);
- }
- }
-
- UpdatePlayerCollection();
}
+ var droppedPlayers = _players.Values.Where(p => onlinePlayers.FirstOrDefault(np => np.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null);
+ foreach (var droppedPlayer in droppedPlayers)
+ {
+ if (droppedPlayer.IsOnline)
+ {
+ droppedPlayer.IsOnline = false;
+
+ var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Leave") ?? "'{0}' left the game.";
+ var message = string.Format(messageFormat, droppedPlayer.PlayerName);
+ output.Add(message);
+ LogEvent(LogEventType.Event, message);
+ LogEvent(LogEventType.All, message);
+ }
+ }
+
+ UpdatePlayerCollection();
+
return output;
}
// This is bound to the UI thread
private void NotifyCommand(ConsoleCommand command)
{
- foreach (var listener in commandListeners)
+ foreach (var listener in _commandListeners)
{
try
{
@@ -446,13 +432,13 @@ namespace ServerManagerTool.Lib
Exception lastException = null;
int retries = 0;
- while (retries < maxCommandRetries)
+ while (retries < _maxCommandRetries)
{
- if (this.console != null)
+ if (_console != null)
{
try
{
- return this.console.SendCommand(command);
+ return _console.SendCommand(command);
}
catch (Exception ex)
{
@@ -475,152 +461,158 @@ namespace ServerManagerTool.Lib
retries++;
}
- this.maxCommandRetries = 10;
- _errorLogger.Error($"Failed to connect to RCON at {this.rconParams.RCONHostIP}:{this.rconParams.RCONPort} with {this.rconParams.RCONPassword}. {lastException.Message}");
- throw new Exception($"Command failed to send after {maxCommandRetries} attempts. Last exception: {lastException.Message}", lastException);
+ _maxCommandRetries = 10;
+ _errorLogger.Error($"Failed to connect to RCON at {_rconParameters.RCONHostIP}:{_rconParameters.RCONPort} with {_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)
+ if (_disposed)
return;
- cancellationTokenSource = new CancellationTokenSource();
- var token = cancellationTokenSource.Token;
+ _cancellationTokenSource = new CancellationTokenSource();
- await UpdatePlayerDetailsAsync(cancellationTokenSource.Token)
- .ContinueWith(async t1 =>
- {
- await TaskUtils.RunOnUIThreadAsync(() =>
- {
- UpdatePlayerCollection();
- });
- }, TaskContinuationOptions.NotOnCanceled)
- .ContinueWith(t2 =>
- {
- var cancelled = cancellationTokenSource.IsCancellationRequested;
- cancellationTokenSource.Dispose();
- cancellationTokenSource = null;
+ await UpdatePlayerDetailsAsync(_cancellationTokenSource.Token);
+ var cancelled = _cancellationTokenSource.IsCancellationRequested;
- if (!cancelled)
- Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t3 => UpdatePlayersAsync());
- });
+ 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 (!string.IsNullOrWhiteSpace(rconParams.InstallDirectory))
+ if (_disposed)
+ return;
+
+ if (string.IsNullOrWhiteSpace(_rconParameters.InstallDirectory))
+ return;
+
+ DataContainer dataContainer = null;
+
+ try
{
- var savedPath = ServerProfile.GetProfileSavePath(rconParams.InstallDirectory, rconParams.AltSaveDirectoryName, rconParams.PGM_Enabled, rconParams.PGM_Name);
- DataContainer dataContainer = null;
- DateTime lastSteamUpdateUtc = DateTime.MinValue;
+ var savedPath = ServerProfile.GetProfileSavePath(_rconParameters.InstallDirectory, _rconParameters.AltSaveDirectoryName, _rconParameters.PGM_Enabled, _rconParameters.PGM_Name);
- try
- {
- // load the player data from the files.
- dataContainer = await DataContainer.CreateAsync(savedPath, savedPath);
- }
- catch (Exception ex)
- {
- _errorLogger.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: CreateAsync. {ex.Message}\r\n{ex.StackTrace}");
- return;
- }
+ // load the player data from the files.
+ dataContainer = await DataContainer.CreateAsync(savedPath, savedPath);
+ }
+ catch (Exception ex)
+ {
+ _errorLogger.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: CreateAsync. {ex.Message}\r\n{ex.StackTrace}");
+ return;
+ }
- token.ThrowIfCancellationRequested();
- 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;
+ if (token.IsCancellationRequested)
+ return;
- this.players.TryGetValue(id, out PlayerInfo player);
- player?.UpdatePlatformData(playerData);
- }
- }, token);
-
- try
- {
- // load the player data from steam
- lastSteamUpdateUtc = await dataContainer.LoadSteamAsync(SteamUtils.SteamWebApiKey, STEAM_UPDATE_INTERVAL);
- }
- catch (Exception ex)
- {
- _errorLogger.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: LoadSteamAsync. {ex.Message}\r\n{ex.StackTrace}");
- }
-
- token.ThrowIfCancellationRequested();
-
- var totalPlayers = dataContainer.Players.Count;
+ await Task.Run(() =>
+ {
+ // update the player data with the latest update value from the players collection
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 = playerData.PlayerId,
- PlayerName = playerData.PlayerName,
- IsValid = true,
- };
+ var id = playerData.PlayerId;
+ if (string.IsNullOrWhiteSpace(id))
+ continue;
- this.players.AddOrUpdate(id, validPlayer, (k, v) => { v.PlayerName = playerData.PlayerName; v.IsValid = true; return v; });
+ _players.TryGetValue(id, out PlayerInfo player);
+ player?.UpdatePlatformData(playerData);
+ }
+ }, token);
+
+ try
+ {
+ // load the player data from steam
+ await dataContainer.LoadSteamAsync(SteamUtils.SteamWebApiKey, STEAM_UPDATE_INTERVAL);
+ }
+ catch (Exception ex)
+ {
+ _errorLogger.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: LoadSteamAsync. {ex.Message}\r\n{ex.StackTrace}");
+ }
+
+ if (token.IsCancellationRequested)
+ return;
+
+ foreach (var playerData in dataContainer.Players)
+ {
+ if (token.IsCancellationRequested)
+ return;
+
+ await Task.Run(async () =>
+ {
+ var id = playerData.PlayerId;
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ id = Path.GetFileNameWithoutExtension(playerData.Filename);
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ _debugLogger.Debug($"{nameof(UpdatePlayerDetailsAsync)} - Error: corrupted profile.\r\n{playerData.Filename}.");
}
else
{
- id = Path.GetFileNameWithoutExtension(playerData.Filename);
- if (string.IsNullOrWhiteSpace(id))
+ var invalidPlayer = new PlayerInfo()
{
- var invalidPlayer = new PlayerInfo()
- {
- PlayerId = id,
- PlayerName = "< corrupted profile >",
- IsValid = false,
- };
+ PlayerId = id,
+ PlayerName = "< corrupted profile >",
+ IsValid = false,
+ };
- this.players.AddOrUpdate(id, invalidPlayer, (k, v) => { v.PlayerName = "< corrupted profile >"; v.IsValid = false; return v; });
- }
- else
- {
- _debugLogger.Debug($"{nameof(UpdatePlayerDetailsAsync)} - Error: corrupted profile.\r\n{playerData.Filename}.");
- }
+ _players.AddOrUpdate(id, invalidPlayer, (k, v) => { v.PlayerName = "< corrupted profile >"; v.IsValid = false; return v; });
}
-
- if (this.players.TryGetValue(id, out PlayerInfo player) && player != null)
+ }
+ else
+ {
+ var validPlayer = new PlayerInfo()
{
- player.UpdateData(playerData);
+ PlayerId = id,
+ PlayerName = playerData.PlayerName,
+ IsValid = true,
+ };
- await TaskUtils.RunOnUIThreadAsync(() =>
- {
- player.IsAdmin = rconParams?.Server?.Profile?.ServerFilesAdmins?.Any(u => u.PlayerId.Equals(player.PlayerId, StringComparison.OrdinalIgnoreCase)) ?? false;
- player.IsWhitelisted = rconParams?.Server?.Profile?.ServerFilesWhitelisted?.Any(u => u.PlayerId.Equals(player.PlayerId, StringComparison.OrdinalIgnoreCase)) ?? false;
- });
- }
- }, token);
- }
+ _players.AddOrUpdate(id, validPlayer, (k, v) => { v.PlayerName = playerData.PlayerName; v.IsValid = true; return v; });
+ }
- token.ThrowIfCancellationRequested();
+ if (_players.TryGetValue(id, out PlayerInfo player) && player != null)
+ {
+ player.UpdateData(playerData);
- // remove any players that do not have a player file.
- 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);
- }
+ await TaskUtils.RunOnUIThreadAsync(() =>
+ {
+ player.IsAdmin = _rconParameters?.Server?.Profile?.ServerFilesAdmins?.Any(u => u.PlayerId.Equals(player.PlayerId, StringComparison.OrdinalIgnoreCase)) ?? false;
+ player.IsWhitelisted = _rconParameters?.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 file.
+ var droppedPlayers = _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)
+ lock (_updatePlayerCollectionLock)
{
- this.Players = new SortableObservableCollection(players.Values);
+ this.Players = new SortableObservableCollection(_players.Values);
this.CountPlayers = this.Players.Count;
this.CountInvalidPlayers = this.Players.Count(p => !p.IsValid);
this.CountOnlinePlayers = this.Players.Count(p => p.IsOnline);
diff --git a/src/ARKServerManager/Lib/ServerStatusWatcher.cs b/src/ARKServerManager/Lib/ServerStatusWatcher.cs
index f2958d83..238034d3 100644
--- a/src/ARKServerManager/Lib/ServerStatusWatcher.cs
+++ b/src/ARKServerManager/Lib/ServerStatusWatcher.cs
@@ -310,8 +310,7 @@ namespace ServerManagerTool.Lib
try
{
- var players = server?.GetPlayers()?.Where(p => !string.IsNullOrWhiteSpace(p.Name?.Trim()));
- onlinePlayerCount = players?.Count() ?? 0;
+ onlinePlayerCount = serverInfo?.Players ?? 0;
}
catch (Exception)
{
diff --git a/src/ARKServerManager/VersionFeed.xml b/src/ARKServerManager/VersionFeed.xml
index 55be8470..00306366 100644
--- a/src/ARKServerManager/VersionFeed.xml
+++ b/src/ARKServerManager/VersionFeed.xml
@@ -9,10 +9,10 @@
urn:uuid:2C48A585-72D2-43FB-8987-6B5F0B3E460F
- 1.1.425 (1.1.425.4)
- 1.1.425.4
+ 1.1.425 (1.1.425.5)
+ 1.1.425.5
- 2022-05-07T00:00:00Z
+ 2022-05-08T00:00:00Z
@@ -24,6 +24,7 @@
Auto Backup Settings - added RCON broadcast mode droplist, so backup processes can send messages via RCON using this mode.
Global Backup Settings - added option to include/exclude the SaveGames folder in the worldsave backup (default exclude).
Global Alert Settings - added new textbox allowing the formatting of the ipaddress and port in the server startup message (default {ipaddress}:{port}).
+
Rcon Window - minor changes to the player list, online player count and server status.
Security Protocol Changes - updated the security protocols to use TLS12 and TLS13.
diff --git a/src/ARKServerManager/VersionFeedBeta.xml b/src/ARKServerManager/VersionFeedBeta.xml
index eeaf610b..aab4b0a6 100644
--- a/src/ARKServerManager/VersionFeedBeta.xml
+++ b/src/ARKServerManager/VersionFeedBeta.xml
@@ -5,7 +5,30 @@
Ark Server Manager Version FeedThis is the Ark Server Manager beta version feed.
- 2022-05-07T00:00:00Z
+ 2022-05-08T00:00:00Z
+
+
+ urn:uuid:CEA21F86-5943-46F9-8807-604695E42A25
+ 1.1.425 (1.1.425.5)
+ 1.1.425.5
+
+ 2022-05-08T00:00:00Z
+
+
+
+ CHANGE
+
+
+
Rcon Window - minor changes to the player list, online player count and server status.
+
+
+
+
+
+ bletch
+ bletch1971@hotmail.com
+
+ urn:uuid:358E0063-27AE-4D5F-BDA5-BD9723EE353E
diff --git a/src/ARKServerManager/Windows/RCONWindow.xaml.cs b/src/ARKServerManager/Windows/RCONWindow.xaml.cs
index bb2e8178..50e87f9b 100644
--- a/src/ARKServerManager/Windows/RCONWindow.xaml.cs
+++ b/src/ARKServerManager/Windows/RCONWindow.xaml.cs
@@ -1,4 +1,5 @@
-using ServerManagerTool.Common.Extensions;
+using ServerManagerTool.Common.Enums;
+using ServerManagerTool.Common.Extensions;
using ServerManagerTool.Common.Lib;
using ServerManagerTool.Common.Utils;
using ServerManagerTool.Enums;
@@ -911,7 +912,7 @@ namespace ServerManagerTool
ConsoleContent.Blocks.Add(b);
}
- private IEnumerable FormatCommandInput(ServerRCON.ConsoleCommand command)
+ private IEnumerable FormatCommandInput(ConsoleCommand command)
{
if (command.command.Equals(ServerRCON.RCON_COMMAND_BROADCAST, StringComparison.OrdinalIgnoreCase))
{
@@ -928,7 +929,7 @@ namespace ServerManagerTool
}
}
- private IEnumerable FormatCommandOutput(ServerRCON.ConsoleCommand command)
+ private IEnumerable FormatCommandOutput(ConsoleCommand command)
{
bool firstLine = true;
@@ -1009,13 +1010,13 @@ namespace ServerManagerTool
private void RenderConnectionStateChange(DependencyPropertyChangedEventArgs e)
{
- var oldStatus = (ServerRCON.ConsoleStatus)e.OldValue;
- var newStatus = (ServerRCON.ConsoleStatus)e.NewValue;
+ var oldStatus = (ConsoleStatus)e.OldValue;
+ var newStatus = (ConsoleStatus)e.NewValue;
if(oldStatus != newStatus)
{
var p = new Paragraph();
- if (newStatus == ServerRCON.ConsoleStatus.Connected)
+ if (newStatus == ConsoleStatus.Connected)
{
p.Inlines.Add(new RCONOutput_ConnectionChanged(true));
}
@@ -1028,7 +1029,7 @@ namespace ServerManagerTool
}
}
- private void RenderRCONCommandOutput(ServerRCON.ConsoleCommand command)
+ private void RenderRCONCommandOutput(ConsoleCommand command)
{
//
// Format output
diff --git a/src/ConanServerManager/Globalization/en-US/en-US.xaml b/src/ConanServerManager/Globalization/en-US/en-US.xaml
index f2cd0595..6d807dc2 100644
--- a/src/ConanServerManager/Globalization/en-US/en-US.xaml
+++ b/src/ConanServerManager/Globalization/en-US/en-US.xaml
@@ -529,8 +529,8 @@
DisconnectedConnected
- Player '{0}' joined the game.
- Player '{0}' left the game.
+ '{0}' joined the game.
+ '{0}' left the game.
diff --git a/src/ConanServerManager/Lib/ServerRcon.cs b/src/ConanServerManager/Lib/ServerRcon.cs
index 4900b4ca..4e5c4113 100644
--- a/src/ConanServerManager/Lib/ServerRcon.cs
+++ b/src/ConanServerManager/Lib/ServerRcon.cs
@@ -29,16 +29,16 @@ namespace ServerManagerTool.Lib
public const string RCON_COMMAND_BROADCAST = "broadcast";
public const string RCON_COMMAND_ALERT = "alert";
public const string RCON_COMMAND_SERVER = "server";
- public const string RCON_COMMAND_LISTPLAYERS = "#managerplayerlist#";
+ public const string RCON_COMMAND_LISTPLAYERS = "listplayers";
public event EventHandler PlayersCollectionUpdated;
+ public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(nameof(Status), typeof(ConsoleStatus), typeof(ServerRcon), new PropertyMetadata(ConsoleStatus.Disconnected));
public static readonly DependencyProperty PlayersProperty = DependencyProperty.Register(nameof(Players), typeof(SortableObservableCollection), 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 locks = new ConcurrentDictionary();
private static readonly char[] lineSplitChars = new char[] { '\n' };
private static readonly char[] argsSplitChars = new char[] { ' ' };
@@ -48,7 +48,7 @@ namespace ServerManagerTool.Lib
private RconParameters _rconParameters;
private QueryMaster.Rcon _console;
- private int maxCommandRetries = 3;
+ private int _maxCommandRetries = 3;
private readonly ConcurrentDictionary _players = new ConcurrentDictionary();
private readonly object _updatePlayerCollectionLock = new object();
@@ -63,16 +63,15 @@ namespace ServerManagerTool.Lib
public ServerRcon(RconParameters parameters)
{
- this._rconParameters = parameters;
+ _rconParameters = parameters;
+
+ _allLogger = App.GetProfileLogger(_rconParameters.ProfileId, "Rcon_All", LogLevel.Info, LogLevel.Info);
+ _chatLogger = App.GetProfileLogger(_rconParameters.ProfileId, "Rcon_Chat", LogLevel.Info, LogLevel.Info);
+ _eventLogger = App.GetProfileLogger(_rconParameters.ProfileId, "Rcon_Event", LogLevel.Info, LogLevel.Info);
+ _debugLogger = App.GetProfileLogger(_rconParameters.ProfileId, "Rcon_Debug", LogLevel.Trace, LogLevel.Debug);
+ _errorLogger = App.GetProfileLogger(_rconParameters.ProfileId, "Rcon_Error", LogLevel.Error, LogLevel.Fatal);
+
this.Players = new SortableObservableCollection();
-
- _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()
@@ -81,18 +80,25 @@ namespace ServerManagerTool.Lib
{
_cancellationTokenSource.Cancel();
}
- await this._commandProcessor.DisposeAsync();
- await this._outputProcessor.DisposeAsync();
- for (int index = this._commandListeners.Count - 1; index >= 0; index--)
+ await _commandProcessor.DisposeAsync();
+ await _outputProcessor.DisposeAsync();
+
+ for (int index = _commandListeners.Count - 1; index >= 0; index--)
{
- this._commandListeners[index].Dispose();
+ _commandListeners[index].Dispose();
}
_disposed = true;
}
#region Properties
+ public ConsoleStatus Status
+ {
+ get { return (ConsoleStatus)GetValue(StatusProperty); }
+ set { SetValue(StatusProperty, value); }
+ }
+
public SortableObservableCollection Players
{
get { return (SortableObservableCollection)GetValue(PlayersProperty); }
@@ -119,6 +125,12 @@ namespace ServerManagerTool.Lib
#endregion
#region Methods
+ public void Initialize()
+ {
+ _commandProcessor.PostAction(AutoPlayerList);
+ UpdatePlayersAsync().DoNotWait();
+ }
+
private void LogEvent(LogEventType eventType, string message)
{
switch (eventType)
@@ -144,32 +156,46 @@ namespace ServerManagerTool.Lib
private bool Reconnect()
{
- if (this._console != null)
+ if (_console != null)
{
- this._console.Dispose();
- this._console = null;
+ _console.Dispose();
+ _console = null;
}
- var endpoint = new IPEndPoint(this._rconParameters.RconHostIP, this._rconParameters.RconPort);
+ var endpoint = new IPEndPoint(_rconParameters.RconHostIP, _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;
+ _console = server.GetControl(_rconParameters.RconPassword);
+ return _console != null;
}
public IDisposable RegisterCommandListener(Action callback)
{
var listener = new CommandListener { Callback = callback, DisposeAction = UnregisterCommandListener };
- this._commandListeners.Add(listener);
+ _commandListeners.Add(listener);
return listener;
}
private void UnregisterCommandListener(CommandListener listener)
{
- this._commandListeners.Remove(listener);
+ _commandListeners.Remove(listener);
}
#endregion
#region Process Methods
+ private Task AutoPlayerList()
+ {
+ return _commandProcessor.PostAction(() =>
+ {
+ ProcessInput(new ConsoleCommand() { rawCommand = RCON_COMMAND_LISTPLAYERS, suppressCommand = true, suppressOutput = true });
+ Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t => _commandProcessor.PostAction(AutoPlayerList)).DoNotWait();
+ });
+ }
+
+ public Task IssueCommand(string userCommand)
+ {
+ return _commandProcessor.PostAction(() => ProcessInput(new ConsoleCommand() { rawCommand = userCommand }));
+ }
+
private bool ProcessInput(ConsoleCommand command)
{
try
@@ -214,14 +240,14 @@ namespace ServerManagerTool.Lib
command.status = ConsoleStatus.Connected;
- this._outputProcessor.PostAction(() => ProcessOutput(command));
+ _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));
+ _outputProcessor.PostAction(() => ProcessOutput(command));
return false;
}
}
@@ -236,11 +262,6 @@ namespace ServerManagerTool.Lib
NotifyCommand(command);
}
- public Task IssueCommand(string userCommand)
- {
- return this._commandProcessor.PostAction(() => ProcessInput(new ConsoleCommand() { rawCommand = userCommand }));
- }
-
// This is bound to the UI thread
private void HandleCommand(ConsoleCommand command)
{
@@ -249,8 +270,8 @@ namespace ServerManagerTool.Lib
//
if (command?.command?.Equals(RCON_COMMAND_LISTPLAYERS, StringComparison.OrdinalIgnoreCase) ?? false)
{
- command.suppressCommand = true;
- command.suppressOutput = false;
+ command.lines = HandleListPlayersCommand(command.lines);
+ command.suppressOutput = command.lines.Count() == 0;
}
if (command?.command?.Equals(RCON_COMMAND_BROADCAST, StringComparison.OrdinalIgnoreCase) ?? false)
@@ -272,6 +293,87 @@ namespace ServerManagerTool.Lib
}
}
+ // This is bound to the UI thread
+ private List HandleListPlayersCommand(IEnumerable commandLines)
+ {
+ var output = new List();
+ var onlinePlayers = new List();
+
+ var playerLines = commandLines?.ToList() ?? new List();
+ if (playerLines.Count > 1)
+ {
+ // remove the first line, as it is a header row
+ playerLines.RemoveAt(0);
+
+ foreach (var line in playerLines)
+ {
+ var elements = line.Split('|');
+ if (elements.Length != 6)
+ // Invalid data. Ignore it.
+ continue;
+
+ var id = elements[3]?.Trim();
+
+ if (onlinePlayers.FirstOrDefault(p => p.PlayerId.Equals(id, StringComparison.OrdinalIgnoreCase)) != null)
+ // Duplicate data. Ignore it.
+ continue;
+
+ var newPlayer = new PlayerInfo()
+ {
+ PlayerId = id,
+ PlayerName = elements[2].Substring(0, elements[2].IndexOf('#')).Trim(),
+ CharacterName = elements[1].Trim(),
+ IsOnline = true,
+ };
+ onlinePlayers.Add(newPlayer);
+
+ var playerJoined = false;
+ _players.AddOrUpdate(newPlayer.PlayerId,
+ (k) =>
+ {
+ playerJoined = true;
+ return newPlayer;
+ },
+ (k, v) =>
+ {
+ playerJoined = !v.IsOnline;
+ v.PlayerName = newPlayer.PlayerName;
+ v.IsOnline = newPlayer.IsOnline;
+ return v;
+ }
+ );
+
+ if (playerJoined)
+ {
+ var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Join") ?? "'{0}' joined the game.";
+ var message = string.Format(messageFormat, newPlayer.CharacterName);
+ output.Add(message);
+ LogEvent(LogEventType.Event, message);
+ LogEvent(LogEventType.All, message);
+ }
+ }
+ }
+
+ var droppedPlayers = _players.Values.Where(p => onlinePlayers.FirstOrDefault(np => np.PlayerId.Equals(p.PlayerId, StringComparison.OrdinalIgnoreCase)) == null);
+ foreach (var droppedPlayer in droppedPlayers)
+ {
+ if (droppedPlayer.IsOnline)
+ {
+ droppedPlayer.IsOnline = false;
+
+ var messageFormat = GlobalizedApplication.Instance.GetResourceString("Player_Leave") ?? "'{0}' left the game.";
+ var message = string.Format(messageFormat, droppedPlayer.CharacterName);
+ output.Add(message);
+ LogEvent(LogEventType.Event, message);
+ LogEvent(LogEventType.All, message);
+ }
+ }
+
+ UpdatePlayerCollection();
+
+ return output;
+ }
+
// This is bound to the UI thread
private void NotifyCommand(ConsoleCommand command)
{
@@ -295,13 +397,13 @@ namespace ServerManagerTool.Lib
Exception lastException = null;
int retries = 0;
- while (retries < maxCommandRetries)
+ while (retries < _maxCommandRetries)
{
- if (this._console != null)
+ if (_console != null)
{
try
{
- return this._console.SendCommand(command);
+ return _console.SendCommand(command);
}
catch (Exception ex)
{
@@ -324,32 +426,25 @@ namespace ServerManagerTool.Lib
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);
+ _maxCommandRetries = 10;
+ _errorLogger?.Error($"Failed to connect to Rcon at {_rconParameters.RconHostIP}:{_rconParameters.RconPort} with {_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)
+ if (_disposed)
return;
_cancellationTokenSource = new CancellationTokenSource();
- var token = _cancellationTokenSource.Token;
- var output = await UpdatePlayerDetailsAsync(_cancellationTokenSource.Token);
+ 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);
- });
+ await TaskUtils.RunOnUIThreadAsync(() => UpdatePlayerCollection());
}
_cancellationTokenSource.Dispose();
@@ -357,19 +452,19 @@ namespace ServerManagerTool.Lib
if (!cancelled)
{
- await Task.Delay(PLAYER_LIST_INTERVAL).ContinueWith(t => UpdatePlayersAsync());
+ await Task.Delay(PLAYER_LIST_INTERVAL)
+ .ContinueWith(t => UpdatePlayersAsync());
}
}
- private async Task> UpdatePlayerDetailsAsync(CancellationToken token)
+ private async Task UpdatePlayerDetailsAsync(CancellationToken token)
{
- if (this._disposed)
- return new List();
+ if (_disposed)
+ return;
if (string.IsNullOrWhiteSpace(_rconParameters.GameFile) || !File.Exists(_rconParameters.GameFile))
- return new List();
+ return;
- var savedPath = ServerProfile.GetProfileSavePath(_rconParameters.InstallDirectory);
DataContainer dataContainer = null;
try
@@ -379,12 +474,12 @@ namespace ServerManagerTool.Lib
}
catch (Exception ex)
{
- _errorLogger?.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: CreateAsync. {ex.Message}\r\n{ex.StackTrace}");
- return new List();
+ _errorLogger.Error($"{nameof(UpdatePlayerDetailsAsync)} - Error: CreateAsync. {ex.Message}\r\n{ex.StackTrace}");
+ return;
}
if (token.IsCancellationRequested)
- return new List();
+ return;
await Task.Run(() =>
{
@@ -395,61 +490,41 @@ namespace ServerManagerTool.Lib
if (string.IsNullOrWhiteSpace(id))
continue;
- this._players.TryGetValue(id, out PlayerInfo player);
+ _players.TryGetValue(id, out PlayerInfo player);
player?.UpdatePlatformData(playerData);
}
}, token);
if (token.IsCancellationRequested)
- return new List();
-
- var totalPlayers = dataContainer.Players.Count;
- var output = new List();
+ return;
foreach (var playerData in dataContainer.Players)
{
- token.ThrowIfCancellationRequested();
+ if (token.IsCancellationRequested)
+ return;
+
await Task.Run(async () =>
{
var id = playerData.PlayerId;
- if (!string.IsNullOrWhiteSpace(id))
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ _debugLogger?.Debug($"{nameof(UpdatePlayerDetailsAsync)} - Error: corrupted profile.\r\n{playerData.CharacterId}.");
+ }
+ else
{
var validPlayer = new PlayerInfo()
{
PlayerId = id,
PlayerName = playerData.PlayerName,
+ CharacterName = playerData.CharacterName,
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}.");
+ _players.AddOrUpdate(id, validPlayer, (k, v) => { v.PlayerName = playerData.PlayerName; v.CharacterName = playerData.CharacterName; v.IsValid = true; return v; });
}
- if (this._players.TryGetValue(id, out PlayerInfo player) && player != null)
+ if (_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(() =>
@@ -461,16 +536,14 @@ namespace ServerManagerTool.Lib
}
if (token.IsCancellationRequested)
- return new List();
+ 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();
+ // remove any players that do not have a player file.
+ var droppedPlayers = _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()
diff --git a/src/ConanServerManager/Lib/ServerStatusWatcher.cs b/src/ConanServerManager/Lib/ServerStatusWatcher.cs
index a3465c1e..cb1894a3 100644
--- a/src/ConanServerManager/Lib/ServerStatusWatcher.cs
+++ b/src/ConanServerManager/Lib/ServerStatusWatcher.cs
@@ -311,8 +311,7 @@ namespace ServerManagerTool.Lib
try
{
- // load the player data from the files.
- onlinePlayerCount = DataContainer.GetOnlinePlayerCount(gameFile);
+ onlinePlayerCount = serverInfo?.Players ?? 0;
}
catch (Exception)
{
diff --git a/src/ConanServerManager/Lib/ViewModel/PlayerInfo.cs b/src/ConanServerManager/Lib/ViewModel/PlayerInfo.cs
index 87469136..c1d4f4cb 100644
--- a/src/ConanServerManager/Lib/ViewModel/PlayerInfo.cs
+++ b/src/ConanServerManager/Lib/ViewModel/PlayerInfo.cs
@@ -115,7 +115,7 @@ namespace ServerManagerTool.Lib.ViewModel
this.CharacterId = playerData?.CharacterId ?? 0L;
this.CharacterName = playerData?.CharacterName;
this.GuildName = playerData?.Guild?.GuildName;
- this.IsOnline = playerData?.Online ?? false;
+ //this.IsOnline = playerData?.Online ?? false;
this.LastOnline = playerData?.LastOnline;
}
diff --git a/src/ConanServerManager/VersionFeed.xml b/src/ConanServerManager/VersionFeed.xml
index 11823158..cb59dd8b 100644
--- a/src/ConanServerManager/VersionFeed.xml
+++ b/src/ConanServerManager/VersionFeed.xml
@@ -5,14 +5,14 @@
Conan Server Manager Version FeedThis is the Conan Server Manager release version feed.
- 2022-05-06T00:00:00Z
+ 2022-05-08T00:00:00Zurn:uuid:AD8ABBB5-093A-4FDB-B473-FCED2DB46781
- 1.1.69 (1.1.69.4)
- 1.1.69.4
+ 1.1.69 (1.1.69.5)
+ 1.1.69.5
- 2022-05-07T00:00:00Z
+ 2022-05-08T00:00:00Z
@@ -27,6 +27,8 @@
Auto Backup Settings - added RCON broadcast mode droplist, so backup processes can send messages via RCON using this mode.
Global Backup Settings - added option to include/exclude the SaveGames folder in the worldsave backup (default exclude).
Global Alert Settings - added new textbox allowing the formatting of the ipaddress and port in the server startup message (default {ipaddress}:{port}).
+
Rcon Window - minor changes to the player list, online player count and server status.
+
Online Player Count - fixed the online player count to display the correct value.
Security Protocol Changes - updated the security protocols to use TLS12 and TLS13.
diff --git a/src/ConanServerManager/VersionFeedBeta.xml b/src/ConanServerManager/VersionFeedBeta.xml
index 42427df4..36cf964d 100644
--- a/src/ConanServerManager/VersionFeedBeta.xml
+++ b/src/ConanServerManager/VersionFeedBeta.xml
@@ -5,7 +5,31 @@
Conan Server Manager Version FeedThis is the Conan Server Manager beta version feed.
- 2022-05-07T00:00:00Z
+ 2022-05-08T00:00:00Z
+
+
+ urn:uuid:68ECEA73-1C9C-4DCB-807B-0D812E986993
+ 1.1.69 (1.1.69.5)
+ 1.1.69.5
+
+ 2022-05-08T00:00:00Z
+
+
+
+ CHANGE
+
+
+
Rcon Window - minor changes to the player list, online player count and server status.
+
Online Player Count - fixed the online player count to display the correct value.