diff --git a/Application/Factories/GameLogReaderFactory.cs b/Application/Factories/GameLogReaderFactory.cs index e035e012b..af95d55de 100644 --- a/Application/Factories/GameLogReaderFactory.cs +++ b/Application/Factories/GameLogReaderFactory.cs @@ -18,14 +18,22 @@ namespace IW4MAdmin.Application.Factories public IGameLogReader CreateGameLogReader(Uri[] logUris, IEventParser eventParser) { var baseUri = logUris[0]; - if (baseUri.Scheme == Uri.UriSchemeHttp) + if (baseUri.Scheme == Uri.UriSchemeHttp || baseUri.Scheme == Uri.UriSchemeHttps) { - return new GameLogReaderHttp(logUris, eventParser, _serviceProvider.GetRequiredService>()); + return new GameLogReaderHttp(logUris, eventParser, + _serviceProvider.GetRequiredService>()); } - else if (baseUri.Scheme == Uri.UriSchemeFile) + if (baseUri.Scheme == Uri.UriSchemeFile) { - return new GameLogReader(baseUri.LocalPath, eventParser, _serviceProvider.GetRequiredService>()); + return new GameLogReader(baseUri.LocalPath, eventParser, + _serviceProvider.GetRequiredService>()); + } + + if (baseUri.Scheme == Uri.UriSchemeNetTcp) + { + return new NetworkGameLogReader(logUris, eventParser, + _serviceProvider.GetRequiredService>()); } throw new NotImplementedException($"No log reader implemented for Uri scheme \"{baseUri.Scheme}\""); diff --git a/Application/IO/NetworkGameLogReader.cs b/Application/IO/NetworkGameLogReader.cs new file mode 100644 index 000000000..a1abef43b --- /dev/null +++ b/Application/IO/NetworkGameLogReader.cs @@ -0,0 +1,96 @@ +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace IW4MAdmin.Application.IO +{ + /// + /// provides capability of reading log files over HTTP + /// + class NetworkGameLogReader : IGameLogReader + { + private readonly IEventParser _eventParser; + private readonly UdpClient _udpClient; + private readonly ILogger _logger; + + public NetworkGameLogReader(Uri[] uris, IEventParser parser, ILogger logger) + { + _eventParser = parser; + try + { + var endPoint = new IPEndPoint(Dns.GetHostAddresses(uris[0].Host).First(), uris[0].Port); + _udpClient = new UdpClient(endPoint); + } + catch (Exception ex) + { + logger.LogError(ex, "Could setup {LogReader}", nameof(NetworkGameLogReader)); + } + + _logger = logger; + } + + public long Length => -1; + + public int UpdateInterval => 500; + + public async Task> ReadEventsFromLog(long fileSizeDiff, long startPosition) + { + if (_udpClient == null) + { + return Enumerable.Empty(); + } + + byte[] buffer; + try + { + buffer = (await _udpClient.ReceiveAsync()).Buffer; + } + catch (Exception ex) + { + _logger.LogError(ex, "Could receive lines for {LogReader}", nameof(NetworkGameLogReader)); + return Enumerable.Empty(); + } + + if (!buffer.Any()) + { + return Enumerable.Empty(); + } + + var logData = Utilities.EncodingType.GetString(buffer); + + if (string.IsNullOrWhiteSpace(logData)) + { + return Enumerable.Empty(); + } + + var lines = logData + .Split('\n') + .Where(line => line.Length > 0); + + var events = new List(); + foreach (var eventLine in lines) + { + try + { + // this trim end should hopefully fix the nasty runaway regex + var gameEvent = _eventParser.GenerateGameEvent(eventLine.TrimEnd('\r')); + events.Add(gameEvent); + } + + catch (Exception ex) + { + _logger.LogError(ex, "Could not properly parse event line from http {eventLine}", eventLine); + } + } + + return events; + } + } +} diff --git a/Plugins/ScriptPlugins/GameInterface.js b/Plugins/ScriptPlugins/GameInterface.js index 240f481e7..7bb0b232e 100644 --- a/Plugins/ScriptPlugins/GameInterface.js +++ b/Plugins/ScriptPlugins/GameInterface.js @@ -334,7 +334,7 @@ const pollForEvents = server => { if (event.subType === 'Meta') { const metaService = _serviceResolver.ResolveService('IMetaService'); - const meta = metaService.GetPersistentMeta(event.data, client).GetAwaiter().GetResult().Value; + const meta = metaService.GetPersistentMeta(event.data, client).GetAwaiter().GetResult(); data[event.data] = meta === null ? '' : meta.Value; } else { data = {