diff --git a/Application/Factories/RConConnectionFactory.cs b/Application/Factories/RConConnectionFactory.cs
index 39982081..14cb1e79 100644
--- a/Application/Factories/RConConnectionFactory.cs
+++ b/Application/Factories/RConConnectionFactory.cs
@@ -1,4 +1,5 @@
using System;
+using System.Net;
using SharedLibraryCore.Interfaces;
using System.Text;
using Integrations.Cod;
@@ -26,21 +27,15 @@ namespace IW4MAdmin.Application.Factories
_serviceProvider = serviceProvider;
}
- ///
- /// creates a new rcon connection instance
- ///
- /// ip address of the server
- /// port of the server
- /// rcon password of the server
- ///
- public IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine)
+ ///
+ public IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine)
{
return rconEngine switch
{
- "COD" => new CodRConConnection(ipAddress, port, password,
+ "COD" => new CodRConConnection(ipEndpoint, password,
_serviceProvider.GetRequiredService>(), GameEncoding),
"Source" => new SourceRConConnection(_serviceProvider.GetRequiredService>(),
- _serviceProvider.GetRequiredService(), ipAddress, port, password),
+ _serviceProvider.GetRequiredService(), ipEndpoint, password),
_ => throw new ArgumentException($"No supported RCon engine available for '{rconEngine}'")
};
}
diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs
index ed4a128b..6a9f500a 100644
--- a/Application/IW4MServer.cs
+++ b/Application/IW4MServer.cs
@@ -11,6 +11,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
@@ -1059,6 +1060,16 @@ namespace IW4MAdmin
public async Task Initialize()
{
+ try
+ {
+ ResolvedIpEndPoint = new IPEndPoint((await Dns.GetHostAddressesAsync(IP)).First(), Port);
+ }
+ catch (Exception ex)
+ {
+ ServerLogger.LogWarning(ex, "Could not resolve hostname or IP for RCon connection {IP}:{Port}", IP, Port);
+ ResolvedIpEndPoint = new IPEndPoint(IPAddress.Parse(IP), Port);
+ }
+
RconParser = Manager.AdditionalRConParsers
.FirstOrDefault(_parser => _parser.Version == ServerConfig.RConParserVersion);
@@ -1068,7 +1079,7 @@ namespace IW4MAdmin
RconParser ??= Manager.AdditionalRConParsers[0];
EventParser ??= Manager.AdditionalEventParsers[0];
- RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine);
+ RemoteConnection = RConConnectionFactory.CreateConnection(ResolvedIpEndPoint, Password, RconParser.RConEngine);
RemoteConnection.SetConfiguration(RconParser);
var version = await this.GetMappedDvarValueOrDefaultAsync("version");
diff --git a/Integrations/Cod/CodRConConnection.cs b/Integrations/Cod/CodRConConnection.cs
index 5192b551..15eb1ad0 100644
--- a/Integrations/Cod/CodRConConnection.cs
+++ b/Integrations/Cod/CodRConConnection.cs
@@ -32,12 +32,12 @@ namespace Integrations.Cod
private readonly ILogger _log;
private readonly Encoding _gameEncoding;
- public CodRConConnection(string ipAddress, int port, string password, ILogger log, Encoding gameEncoding)
+ public CodRConConnection(IPEndPoint ipEndpoint, string password, ILogger log, Encoding gameEncoding)
{
- Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
- _gameEncoding = gameEncoding;
RConPassword = password;
+ _gameEncoding = gameEncoding;
_log = log;
+ Endpoint = ipEndpoint;
}
public void SetConfiguration(IRConParser parser)
diff --git a/Integrations/Source/Interfaces/IRConClientFactory.cs b/Integrations/Source/Interfaces/IRConClientFactory.cs
index 92b56504..4d33a9cc 100644
--- a/Integrations/Source/Interfaces/IRConClientFactory.cs
+++ b/Integrations/Source/Interfaces/IRConClientFactory.cs
@@ -1,9 +1,10 @@
-using RconSharp;
+using System.Net;
+using RconSharp;
namespace Integrations.Source.Interfaces
{
public interface IRConClientFactory
{
- RconClient CreateClient(string hostname, int port);
+ RconClient CreateClient(IPEndPoint ipEndPoint);
}
}
\ No newline at end of file
diff --git a/Integrations/Source/RConClientFactory.cs b/Integrations/Source/RConClientFactory.cs
index 35e5347b..7aa69b53 100644
--- a/Integrations/Source/RConClientFactory.cs
+++ b/Integrations/Source/RConClientFactory.cs
@@ -1,13 +1,14 @@
-using Integrations.Source.Interfaces;
+using System.Net;
+using Integrations.Source.Interfaces;
using RconSharp;
namespace Integrations.Source
{
public class RConClientFactory : IRConClientFactory
{
- public RconClient CreateClient(string hostname, int port)
+ public RconClient CreateClient(IPEndPoint ipEndPoint)
{
- return RconClient.Create(hostname, port);
+ return RconClient.Create(ipEndPoint.Address.ToString(), ipEndPoint.Port);
}
}
}
\ No newline at end of file
diff --git a/Integrations/Source/SourceRConConnection.cs b/Integrations/Source/SourceRConConnection.cs
index c8f3661a..5334a4b0 100644
--- a/Integrations/Source/SourceRConConnection.cs
+++ b/Integrations/Source/SourceRConConnection.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@@ -20,8 +21,7 @@ namespace Integrations.Source
{
private readonly ILogger _logger;
private readonly string _password;
- private readonly string _hostname;
- private readonly int _port;
+ private readonly IPEndPoint _ipEndPoint;
private readonly IRConClientFactory _rconClientFactory;
private readonly SemaphoreSlim _activeQuery;
@@ -34,13 +34,12 @@ namespace Integrations.Source
private bool _needNewSocket = true;
public SourceRConConnection(ILogger logger, IRConClientFactory rconClientFactory,
- string hostname, int port, string password)
+ IPEndPoint ipEndPoint, string password)
{
_rconClientFactory = rconClientFactory;
_password = password;
- _hostname = hostname;
- _port = port;
_logger = logger;
+ _ipEndPoint = ipEndPoint;
_activeQuery = new SemaphoreSlim(1, 1);
}
@@ -67,12 +66,12 @@ namespace Integrations.Source
// ignored
}
- _rconClient = _rconClientFactory.CreateClient(_hostname, _port);
+ _rconClient = _rconClientFactory.CreateClient(_ipEndPoint);
_authenticated = false;
_needNewSocket = false;
}
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{
_logger.LogDebug("Connecting to RCon socket");
}
@@ -90,7 +89,7 @@ namespace Integrations.Source
parameters = parameters.ReplaceUnfriendlyCharacters();
parameters = parameters.StripColors();
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{
_logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
}
@@ -98,7 +97,7 @@ namespace Integrations.Source
var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket)
.WithTimeout(ConnectionTimeout);
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", $"{_ipEndPoint}"))
{
_logger.LogDebug("Received RCon response {Response}", response);
}
@@ -115,7 +114,7 @@ namespace Integrations.Source
catch (SocketException ex)
{
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{
_logger.LogError(ex, "Socket exception encountered while attempting to communicate with server");
}
@@ -128,7 +127,7 @@ namespace Integrations.Source
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
ex.GetType() != typeof(ServerException))
{
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{
_logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters);
}
@@ -160,7 +159,7 @@ namespace Integrations.Source
{
if (!_authenticated)
{
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{
_logger.LogDebug("Authenticating to RCon socket");
}
@@ -170,7 +169,7 @@ namespace Integrations.Source
if (!_authenticated)
{
- using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
+ using (LogContext.PushProperty("Server", _ipEndPoint.ToString()))
{
_logger.LogError("Could not login to server");
}
diff --git a/SharedLibraryCore/Configuration/ServerConfiguration.cs b/SharedLibraryCore/Configuration/ServerConfiguration.cs
index 90b4f395..bc1e1ff0 100644
--- a/SharedLibraryCore/Configuration/ServerConfiguration.cs
+++ b/SharedLibraryCore/Configuration/ServerConfiguration.cs
@@ -87,12 +87,8 @@ namespace SharedLibraryCore.Configuration
while (string.IsNullOrEmpty(IPAddress))
{
- string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
-
- if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip))
- {
- IPAddress = input;
- }
+ var input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
+ IPAddress = input;
}
Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue);
diff --git a/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs b/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs
index 5be31596..decc448d 100644
--- a/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs
+++ b/SharedLibraryCore/Configuration/Validation/ServerConfigurationValidator.cs
@@ -10,10 +10,6 @@ namespace SharedLibraryCore.Configuration.Validation
{
public ServerConfigurationValidator()
{
- RuleFor(_server => _server.IPAddress)
- .NotEmpty()
- .Must(_address => IPAddress.TryParse(_address, out _));
-
RuleFor(_server => _server.Port)
.InclusiveBetween(1, ushort.MaxValue);
diff --git a/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs b/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs
index 025a5a5a..3179417a 100644
--- a/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs
+++ b/SharedLibraryCore/Interfaces/IRConConnectionFactory.cs
@@ -1,4 +1,6 @@
-namespace SharedLibraryCore.Interfaces
+using System.Net;
+
+namespace SharedLibraryCore.Interfaces
{
///
/// defines the capabilities of an RCon connection factory
@@ -8,11 +10,10 @@
///
/// creates an rcon connection instance
///
- /// ip address of the server
- /// port of the server
+ /// ip address and port of the server
/// password of the server
/// engine to create the rcon connection to
/// instance of rcon connection
- IRConConnection CreateConnection(string ipAddress, int port, string password, string rconEngine);
+ IRConConnection CreateConnection(IPEndPoint ipEndpoint, string password, string rconEngine);
}
}
diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs
index 02907d5e..fba519ea 100644
--- a/SharedLibraryCore/Server.cs
+++ b/SharedLibraryCore/Server.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -58,7 +59,9 @@ namespace SharedLibraryCore
InitializeAutoMessages();
}
- public long EndPoint => Convert.ToInt64($"{IP.Replace(".", "")}{Port}");
+ public long EndPoint => IPAddress.TryParse(IP, out _)
+ ? Convert.ToInt64($"{IP.Replace(".", "")}{Port}")
+ : $"{IP.Replace(".", "")}{Port}".GetStableHashCode();
///
/// Returns list of all current players
@@ -303,7 +306,7 @@ namespace SharedLibraryCore
{
get
{
- return Clients.Where(p => p != null/* && !p.IsBot*/).Count();
+ return Clients.Count(p => p != null && !p.IsBot);
}
}
public int MaxClients { get; protected set; }
@@ -320,7 +323,11 @@ namespace SharedLibraryCore
public SemaphoreSlim EventProcessing { get; private set; }
// Internal
+ ///
+ /// this is actually the hostname now
+ ///
public string IP { get; protected set; }
+ public IPEndPoint ResolvedIpEndPoint { get; protected set; }
public string Version { get; protected set; }
public bool IsInitialized { get; set; }
protected readonly ILogger ServerLogger;
diff --git a/WebfrontCore/ViewComponents/ServerListViewComponent.cs b/WebfrontCore/ViewComponents/ServerListViewComponent.cs
index 1c2af9ba..6ebd71d5 100644
--- a/WebfrontCore/ViewComponents/ServerListViewComponent.cs
+++ b/WebfrontCore/ViewComponents/ServerListViewComponent.cs
@@ -12,7 +12,7 @@ namespace WebfrontCore.ViewComponents
{
public IViewComponentResult Invoke(Game? game)
{
- var servers = Program.Manager.GetServers().Where(_server => !game.HasValue ? true : _server.GameName == game);
+ var servers = Program.Manager.GetServers().Where(_server => !game.HasValue || _server.GameName == game);
var serverInfo = servers.Select(s => new ServerInfo()
{
@@ -36,8 +36,8 @@ namespace WebfrontCore.ViewComponents
}).ToList(),
ChatHistory = s.ChatHistory.ToList(),
Online = !s.Throttled,
- IPAddress = $"{(IPAddress.Parse(s.IP).IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}",
- ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(IPAddress.Parse(s.IP).IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port)
+ IPAddress = $"{(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP)}:{s.Port}",
+ ConnectProtocolUrl = s.EventParser.URLProtocolFormat.FormatExt(s.ResolvedIpEndPoint.Address.IsInternal() ? Program.Manager.ExternalIPAddress : s.IP, s.Port)
}).ToList();
return View("_List", serverInfo);
}
diff --git a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml
index b0751db9..7114804a 100644
--- a/WebfrontCore/Views/Client/Statistics/Advanced.cshtml
+++ b/WebfrontCore/Views/Client/Statistics/Advanced.cshtml
@@ -139,7 +139,7 @@
: null;
var headShots = allPerServer.Any()
- ? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey || hit.MeansOfDeath?.Name == headshotKey2).Sum(hit => hit.HitCount)
+ ? allPerServer.Where(hit => hit.MeansOfDeath?.Name == headshotKey || hit.HitLocation?.Name == headshotKey2).Sum(hit => hit.HitCount)
: (int?) null; // want to default to -- in ui instead of 0
var meleeKills = allPerServer.Any()