[tweaks and fixes]
reenable tekno support address vagrant thread issue refactor game log reader creation to follow better practices fix bot issues/address how guids are generated for bots/none provided
This commit is contained in:
parent
b49592d666
commit
267e0b8cbe
1
.gitignore
vendored
1
.gitignore
vendored
@ -242,3 +242,4 @@ launchSettings.json
|
||||
/WebfrontCore/wwwroot/font
|
||||
/Plugins/Tests/TestSourceFiles
|
||||
/Tests/ApplicationTests/Files/GameEvents.json
|
||||
/Tests/ApplicationTests/Files/replay.json
|
||||
|
@ -10,7 +10,7 @@
|
||||
<Company>Forever None</Company>
|
||||
<Product>IW4MAdmin</Product>
|
||||
<Description>IW4MAdmin is a complete server administration tool for IW4x and most Call of Duty® dedicated servers</Description>
|
||||
<Copyright>2019</Copyright>
|
||||
<Copyright>2020</Copyright>
|
||||
<PackageLicenseUrl>https://github.com/RaidMax/IW4M-Admin/blob/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageProjectUrl>https://raidmax.org/IW4MAdmin</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/RaidMax/IW4M-Admin</RepositoryUrl>
|
||||
|
@ -31,7 +31,7 @@ namespace IW4MAdmin.Application
|
||||
private readonly ConcurrentBag<Server> _servers;
|
||||
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
|
||||
public ILogger Logger => GetLogger(0);
|
||||
public bool Running { get; private set; }
|
||||
public bool IsRunning { get; private set; }
|
||||
public bool IsInitialized { get; private set; }
|
||||
public DateTime StartTime { get; private set; }
|
||||
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
|
||||
@ -50,7 +50,6 @@ namespace IW4MAdmin.Application
|
||||
readonly AliasService AliasSvc;
|
||||
readonly PenaltyService PenaltySvc;
|
||||
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||
GameEventHandler Handler;
|
||||
readonly IPageList PageList;
|
||||
private readonly Dictionary<long, ILogger> _loggers = new Dictionary<long, ILogger>();
|
||||
private readonly MetaService _metaService;
|
||||
@ -62,11 +61,13 @@ namespace IW4MAdmin.Application
|
||||
private readonly IGameServerInstanceFactory _serverInstanceFactory;
|
||||
private readonly IParserRegexFactory _parserRegexFactory;
|
||||
private readonly IEnumerable<IRegisterEvent> _customParserEvents;
|
||||
private readonly IEventHandler _eventHandler;
|
||||
|
||||
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands,
|
||||
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
|
||||
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory,
|
||||
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents)
|
||||
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
||||
IEventHandler eventHandler)
|
||||
{
|
||||
MiddlewareActionHandler = actionHandler;
|
||||
_servers = new ConcurrentBag<Server>();
|
||||
@ -90,6 +91,7 @@ namespace IW4MAdmin.Application
|
||||
_serverInstanceFactory = serverInstanceFactory;
|
||||
_parserRegexFactory = parserRegexFactory;
|
||||
_customParserEvents = customParserEvents;
|
||||
_eventHandler = eventHandler;
|
||||
Plugins = plugins;
|
||||
}
|
||||
|
||||
@ -255,7 +257,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
Running = true;
|
||||
IsRunning = true;
|
||||
ExternalIPAddress = await Utilities.GetExternalIP();
|
||||
|
||||
#region PLUGINS
|
||||
@ -589,9 +591,6 @@ namespace IW4MAdmin.Application
|
||||
|
||||
async Task Init(ServerConfiguration Conf)
|
||||
{
|
||||
// setup the event handler after the class is initialized
|
||||
Handler = new GameEventHandler(this);
|
||||
|
||||
try
|
||||
{
|
||||
// todo: this might not always be an IW4MServer
|
||||
@ -610,7 +609,7 @@ namespace IW4MAdmin.Application
|
||||
Owner = ServerInstance
|
||||
};
|
||||
|
||||
Handler.AddEvent(e);
|
||||
AddEvent(e);
|
||||
successServers++;
|
||||
}
|
||||
|
||||
@ -726,7 +725,7 @@ namespace IW4MAdmin.Application
|
||||
public void Stop()
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
Running = false;
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
@ -782,9 +781,9 @@ namespace IW4MAdmin.Application
|
||||
return ConfigHandler;
|
||||
}
|
||||
|
||||
public IEventHandler GetEventHandler()
|
||||
public void AddEvent(GameEvent gameEvent)
|
||||
{
|
||||
return Handler;
|
||||
_eventHandler.HandleEvent(this, gameEvent);
|
||||
}
|
||||
|
||||
public IPageList GetPageList()
|
||||
|
@ -23,26 +23,26 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
GameDirectory = "main",
|
||||
};
|
||||
|
||||
Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{1,32});([0-9]+);(.+);(.*)$";
|
||||
Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(.+);(.*)$";
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.Message, 5);
|
||||
|
||||
Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||
Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(.*)$";
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
|
||||
Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||
Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(.*)$";
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
|
||||
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||
@ -57,7 +57,7 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
||||
|
||||
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||
@ -118,7 +118,13 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (message.Length > 0)
|
||||
{
|
||||
long originId = matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
string originIdString = matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||
string originName = matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginName]].ToString();
|
||||
|
||||
long originId = originIdString.IsBotGuid() ?
|
||||
originName.GenerateGuidFromString() :
|
||||
originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
|
||||
int clientNumber = int.Parse(matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
||||
|
||||
// todo: these need to defined outside of here
|
||||
@ -132,7 +138,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Message = message,
|
||||
Extra = logLine,
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.Origin,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
|
||||
@ -144,7 +151,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Message = message,
|
||||
Extra = logLine,
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.Origin,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -156,8 +164,18 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
long originId = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle, 1);
|
||||
long targetId = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle, 1);
|
||||
string originIdString = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||
string targetIdString = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString();
|
||||
string originName = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginName]].ToString();
|
||||
string targetName = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetName]].ToString();
|
||||
|
||||
long originId = originIdString.IsBotGuid() ?
|
||||
originName.GenerateGuidFromString() :
|
||||
originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
||||
long targetId = targetIdString.IsBotGuid() ?
|
||||
targetName.GenerateGuidFromString() :
|
||||
targetIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
||||
|
||||
int originClientNumber = int.Parse(match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
||||
int targetClientNumber = int.Parse(match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetClientNumber]]);
|
||||
|
||||
@ -168,7 +186,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Origin = new EFClient() { NetworkId = originId, ClientNumber = originClientNumber },
|
||||
Target = new EFClient() { NetworkId = targetId, ClientNumber = targetClientNumber },
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -179,8 +198,18 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
long originId = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle, 1);
|
||||
long targetId = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle, 1);
|
||||
string originIdString = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||
string targetIdString = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString();
|
||||
string originName = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginName]].ToString();
|
||||
string targetName = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetName]].ToString();
|
||||
|
||||
long originId = originIdString.IsBotGuid() ?
|
||||
originName.GenerateGuidFromString() :
|
||||
originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
||||
long targetId = targetIdString.IsBotGuid() ?
|
||||
targetName.GenerateGuidFromString() :
|
||||
targetIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
||||
|
||||
int originClientNumber = int.Parse(match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
||||
int targetClientNumber = int.Parse(match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetClientNumber]]);
|
||||
|
||||
@ -191,7 +220,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Origin = new EFClient() { NetworkId = originId, ClientNumber = originClientNumber },
|
||||
Target = new EFClient() { NetworkId = targetId, ClientNumber = targetClientNumber },
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -202,6 +232,13 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
string originIdString = match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||
string originName = match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]].ToString();
|
||||
|
||||
long networkId = originIdString.IsBotGuid() ?
|
||||
originName.GenerateGuidFromString() :
|
||||
originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
@ -212,13 +249,14 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
Name = match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().TrimNewLine(),
|
||||
},
|
||||
NetworkId = match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle),
|
||||
NetworkId = networkId,
|
||||
ClientNumber = Convert.ToInt32(match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()),
|
||||
State = EFClient.ClientState.Connecting,
|
||||
},
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
||||
IsBlocking = true,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -229,6 +267,13 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
string originIdString = match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||
string originName = match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]].ToString();
|
||||
|
||||
long networkId = originIdString.IsBotGuid() ?
|
||||
originName.GenerateGuidFromString() :
|
||||
originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
@ -239,13 +284,14 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
Name = match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().TrimNewLine()
|
||||
},
|
||||
NetworkId = match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertGuidToLong(Configuration.GuidNumberStyle),
|
||||
NetworkId = networkId,
|
||||
ClientNumber = Convert.ToInt32(match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()),
|
||||
State = EFClient.ClientState.Disconnecting
|
||||
},
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
||||
IsBlocking = true,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -259,7 +305,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Origin = Utilities.IW4MAdminClient(),
|
||||
Target = Utilities.IW4MAdminClient(),
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
|
||||
@ -275,7 +322,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Target = Utilities.IW4MAdminClient(),
|
||||
Extra = dump.DictionaryFromKeyValue(),
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
|
||||
@ -290,7 +338,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Type = GameEvent.EventType.Other,
|
||||
Data = logLine,
|
||||
Subtype = eventModifier.Item1,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
});
|
||||
}
|
||||
|
||||
@ -307,7 +356,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Origin = Utilities.IW4MAdminClient(),
|
||||
Target = Utilities.IW4MAdminClient(),
|
||||
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
||||
GameTime = gameTime
|
||||
GameTime = gameTime,
|
||||
Source = GameEvent.EventSource.Log
|
||||
};
|
||||
}
|
||||
|
||||
|
33
Application/Factories/GameLogReaderFactory.cs
Normal file
33
Application/Factories/GameLogReaderFactory.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using IW4MAdmin.Application.IO;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace IW4MAdmin.Application.Factories
|
||||
{
|
||||
public class GameLogReaderFactory : IGameLogReaderFactory
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public GameLogReaderFactory(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IGameLogReader CreateGameLogReader(Uri[] logUris, IEventParser eventParser)
|
||||
{
|
||||
var baseUri = logUris[0];
|
||||
if (baseUri.Scheme == Uri.UriSchemeHttp)
|
||||
{
|
||||
return new GameLogReaderHttp(logUris, eventParser, _serviceProvider.GetRequiredService<ILogger>());
|
||||
}
|
||||
|
||||
else if (baseUri.Scheme == Uri.UriSchemeFile)
|
||||
{
|
||||
return new GameLogReader(baseUri.LocalPath, eventParser, _serviceProvider.GetRequiredService<ILogger>());
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"No log reader implemented for Uri scheme \"{baseUri.Scheme}\"");
|
||||
}
|
||||
}
|
||||
}
|
@ -12,16 +12,18 @@ namespace IW4MAdmin.Application.Factories
|
||||
{
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private readonly IRConConnectionFactory _rconConnectionFactory;
|
||||
private readonly IGameLogReaderFactory _gameLogReaderFactory;
|
||||
|
||||
/// <summary>
|
||||
/// base constructor
|
||||
/// </summary>
|
||||
/// <param name="translationLookup"></param>
|
||||
/// <param name="rconConnectionFactory"></param>
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup, IRConConnectionFactory rconConnectionFactory)
|
||||
public GameServerInstanceFactory(ITranslationLookup translationLookup, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory)
|
||||
{
|
||||
_translationLookup = translationLookup;
|
||||
_rconConnectionFactory = rconConnectionFactory;
|
||||
_gameLogReaderFactory = gameLogReaderFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -32,7 +34,7 @@ namespace IW4MAdmin.Application.Factories
|
||||
/// <returns></returns>
|
||||
public Server CreateServer(ServerConfiguration config, IManager manager)
|
||||
{
|
||||
return new IW4MServer(manager, config, _translationLookup, _rconConnectionFactory);
|
||||
return new IW4MServer(manager, config, _translationLookup, _rconConnectionFactory, _gameLogReaderFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
using IW4MAdmin.Application.Misc;
|
||||
using Newtonsoft.Json;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
class GameEventHandler : IEventHandler
|
||||
public class GameEventHandler : IEventHandler
|
||||
{
|
||||
private const int MAX_CONCURRENT_EVENTS = 10;
|
||||
private readonly ApplicationManager _manager;
|
||||
private readonly SemaphoreSlim _processingEvents;
|
||||
private readonly EventLog _eventLog;
|
||||
private static readonly GameEvent.EventType[] overrideEvents = new[]
|
||||
{
|
||||
GameEvent.EventType.Connect,
|
||||
@ -22,39 +22,12 @@ namespace IW4MAdmin.Application
|
||||
GameEvent.EventType.Stop
|
||||
};
|
||||
|
||||
public GameEventHandler(IManager mgr)
|
||||
public GameEventHandler()
|
||||
{
|
||||
_manager = (ApplicationManager)mgr;
|
||||
_processingEvents = new SemaphoreSlim(MAX_CONCURRENT_EVENTS, MAX_CONCURRENT_EVENTS);
|
||||
_eventLog = new EventLog();
|
||||
}
|
||||
|
||||
private Task GameEventHandler_GameEventAdded(object sender, GameEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this is not elegant and there's probably a much better way to do it, but it works for now
|
||||
_processingEvents.Wait();
|
||||
EventApi.OnGameEvent(sender, args);
|
||||
return _manager.ExecuteEvent(args.Event);
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (_processingEvents.CurrentCount < MAX_CONCURRENT_EVENTS)
|
||||
{
|
||||
_processingEvents.Release();
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void AddEvent(GameEvent gameEvent)
|
||||
public void HandleEvent(IManager manager, GameEvent gameEvent)
|
||||
{
|
||||
#if DEBUG
|
||||
ThreadPool.GetMaxThreads(out int workerThreads, out int n);
|
||||
@ -62,12 +35,23 @@ namespace IW4MAdmin.Application
|
||||
gameEvent.Owner.Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
|
||||
|
||||
#endif
|
||||
if (_manager.Running || overrideEvents.Contains(gameEvent.Type))
|
||||
if (manager.IsRunning || overrideEvents.Contains(gameEvent.Type))
|
||||
{
|
||||
#if DEBUG
|
||||
gameEvent.Owner.Logger.WriteDebug($"Adding event with id {gameEvent.Id}");
|
||||
#endif
|
||||
Task.Run(() => GameEventHandler_GameEventAdded(this, new GameEventArgs(null, false, gameEvent)));
|
||||
|
||||
EventApi.OnGameEvent(gameEvent);
|
||||
Task.Factory.StartNew(() => manager.ExecuteEvent(gameEvent));
|
||||
|
||||
/*if (!_eventLog.ContainsKey(gameEvent.Owner.EndPoint))
|
||||
{
|
||||
_eventLog.Add(gameEvent.Owner.EndPoint,new List<GameEvent>());
|
||||
}
|
||||
_eventLog[gameEvent.Owner.EndPoint].Add(gameEvent);
|
||||
string serializedEvents = JsonConvert.SerializeObject(_eventLog, EventLog.BuildVcrSerializationSettings());
|
||||
System.IO.File.WriteAllText("output.json", serializedEvents);*/
|
||||
//Task.Run(() => GameEventHandler_GameEventAdded(this, new GameEventArgs(null, false, gameEvent)));
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
|
@ -13,17 +13,9 @@ namespace IW4MAdmin.Application.IO
|
||||
private readonly IGameLogReader _reader;
|
||||
private readonly bool _ignoreBots;
|
||||
|
||||
class EventState
|
||||
public GameLogEventDetection(Server server, Uri[] gameLogUris, IGameLogReaderFactory gameLogReaderFactory)
|
||||
{
|
||||
public ILogger Log { get; set; }
|
||||
public string ServerId { get; set; }
|
||||
}
|
||||
|
||||
public GameLogEventDetection(Server server, string gameLogPath, Uri gameLogServerUri, IGameLogReader reader = null)
|
||||
{
|
||||
_reader = gameLogServerUri != null
|
||||
? reader ?? new GameLogReaderHttp(gameLogServerUri, gameLogPath, server.EventParser)
|
||||
: reader ?? new GameLogReader(gameLogPath, server.EventParser);
|
||||
_reader = gameLogReaderFactory.CreateGameLogReader(gameLogUris, server.EventParser);
|
||||
_server = server;
|
||||
_ignoreBots = server?.Manager.GetApplicationSettings().Configuration().IgnoreBots ?? false;
|
||||
}
|
||||
@ -70,7 +62,7 @@ namespace IW4MAdmin.Application.IO
|
||||
return;
|
||||
}
|
||||
|
||||
var events = await _reader.ReadEventsFromLog(_server, fileDiff, previousFileSize);
|
||||
var events = await _reader.ReadEventsFromLog(fileDiff, previousFileSize);
|
||||
|
||||
foreach (var gameEvent in events)
|
||||
{
|
||||
@ -84,9 +76,9 @@ namespace IW4MAdmin.Application.IO
|
||||
// we don't want to add the event if ignoreBots is on and the event comes from a bot
|
||||
if (!_ignoreBots || (_ignoreBots && !((gameEvent.Origin?.IsBot ?? false) || (gameEvent.Target?.IsBot ?? false))))
|
||||
{
|
||||
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != 1)
|
||||
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != Utilities.WORLD_ID)
|
||||
{
|
||||
gameEvent.Origin = _server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId);
|
||||
gameEvent.Origin = _server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId);;
|
||||
}
|
||||
|
||||
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Target) == GameEvent.EventRequiredEntity.Target)
|
||||
@ -104,7 +96,7 @@ namespace IW4MAdmin.Application.IO
|
||||
gameEvent.Target.CurrentServer = _server;
|
||||
}
|
||||
|
||||
_server.Manager.GetEventHandler().AddEvent(gameEvent);
|
||||
_server.Manager.AddEvent(gameEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,18 +13,20 @@ namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
private readonly IEventParser _parser;
|
||||
private readonly string _logFile;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public long Length => new FileInfo(_logFile).Length;
|
||||
|
||||
public int UpdateInterval => 300;
|
||||
|
||||
public GameLogReader(string logFile, IEventParser parser)
|
||||
public GameLogReader(string logFile, IEventParser parser, ILogger logger)
|
||||
{
|
||||
_logFile = logFile;
|
||||
_parser = parser;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||
public async Task<IEnumerable<GameEvent>> ReadEventsFromLog(long fileSizeDiff, long startPosition)
|
||||
{
|
||||
// allocate the bytes for the new log lines
|
||||
List<string> logLines = new List<string>();
|
||||
@ -34,7 +36,7 @@ namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
byte[] buff = new byte[fileSizeDiff];
|
||||
fs.Seek(startPosition, SeekOrigin.Begin);
|
||||
await fs.ReadAsync(buff, 0, (int)fileSizeDiff, server.Manager.CancellationToken);
|
||||
await fs.ReadAsync(buff, 0, (int)fileSizeDiff);
|
||||
var stringBuilder = new StringBuilder();
|
||||
char[] charBuff = Utilities.EncodingType.GetChars(buff);
|
||||
|
||||
@ -71,9 +73,9 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
server.Logger.WriteWarning("Could not properly parse event line");
|
||||
server.Logger.WriteDebug(e.Message);
|
||||
server.Logger.WriteDebug(eventLine);
|
||||
_logger.WriteWarning("Could not properly parse event line");
|
||||
_logger.WriteDebug(e.Message);
|
||||
_logger.WriteDebug(eventLine);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,43 +5,42 @@ using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Utilities;
|
||||
|
||||
namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// provides capibility of reading log files over HTTP
|
||||
/// provides capability of reading log files over HTTP
|
||||
/// </summary>
|
||||
class GameLogReaderHttp : IGameLogReader
|
||||
{
|
||||
private readonly IEventParser _eventParser;
|
||||
private readonly IGameLogServer _logServerApi;
|
||||
readonly string logPath;
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _safeLogPath;
|
||||
private string lastKey = "next";
|
||||
|
||||
public GameLogReaderHttp(Uri gameLogServerUri, string logPath, IEventParser parser)
|
||||
public GameLogReaderHttp(Uri[] gameLogServerUris, IEventParser parser, ILogger logger)
|
||||
{
|
||||
this.logPath = logPath.ToBase64UrlSafeString();
|
||||
_eventParser = parser;
|
||||
_logServerApi = RestClient.For<IGameLogServer>(gameLogServerUri);
|
||||
_logServerApi = RestClient.For<IGameLogServer>(gameLogServerUris[0].ToString());
|
||||
_safeLogPath = gameLogServerUris[1].LocalPath.ToBase64UrlSafeString();
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public long Length => -1;
|
||||
|
||||
public int UpdateInterval => 500;
|
||||
|
||||
public async Task<IEnumerable<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||
public async Task<IEnumerable<GameEvent>> ReadEventsFromLog(long fileSizeDiff, long startPosition)
|
||||
{
|
||||
var events = new List<GameEvent>();
|
||||
string b64Path = logPath;
|
||||
var response = await _logServerApi.Log(b64Path, lastKey);
|
||||
var response = await _logServerApi.Log(_safeLogPath, lastKey);
|
||||
lastKey = response.NextKey;
|
||||
|
||||
if (!response.Success && string.IsNullOrEmpty(lastKey))
|
||||
{
|
||||
server.Logger.WriteError($"Could not get log server info of {logPath}/{b64Path} ({server.LogPath})");
|
||||
_logger.WriteError($"Could not get log server info of {_safeLogPath}");
|
||||
return events;
|
||||
}
|
||||
|
||||
@ -63,9 +62,9 @@ namespace IW4MAdmin.Application.IO
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
server.Logger.WriteError("Could not properly parse event line from http");
|
||||
server.Logger.WriteDebug(e.Message);
|
||||
server.Logger.WriteDebug(eventLine);
|
||||
_logger.WriteError("Could not properly parse event line from http");
|
||||
_logger.WriteDebug(e.Message);
|
||||
_logger.WriteDebug(eventLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -31,7 +32,7 @@ namespace IW4MAdmin
|
||||
public int Id { get; private set; }
|
||||
|
||||
public IW4MServer(IManager mgr, ServerConfiguration cfg, ITranslationLookup lookup,
|
||||
IRConConnectionFactory connectionFactory) : base(mgr, connectionFactory, cfg)
|
||||
IRConConnectionFactory connectionFactory, IGameLogReaderFactory gameLogReaderFactory) : base(cfg, mgr, connectionFactory, gameLogReaderFactory)
|
||||
{
|
||||
_translationLookup = lookup;
|
||||
}
|
||||
@ -77,7 +78,7 @@ namespace IW4MAdmin
|
||||
Type = GameEvent.EventType.Connect
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
return client;
|
||||
}
|
||||
|
||||
@ -104,7 +105,7 @@ namespace IW4MAdmin
|
||||
Type = GameEvent.EventType.Disconnect
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
#if DEBUG == true
|
||||
}
|
||||
#endif
|
||||
@ -176,6 +177,8 @@ namespace IW4MAdmin
|
||||
await command.ExecuteAsync(E);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var pluginTasks = Manager.Plugins.Where(_plugin => _plugin.Name != "Login").Select(async _plugin =>
|
||||
{
|
||||
try
|
||||
@ -186,7 +189,11 @@ namespace IW4MAdmin
|
||||
return;
|
||||
}
|
||||
|
||||
await _plugin.OnEventAsync(E, this);
|
||||
using (var tokenSource = new CancellationTokenSource())
|
||||
{
|
||||
tokenSource.CancelAfter(Utilities.DefaultCommandTimeout);
|
||||
await (_plugin.OnEventAsync(E, this)).WithWaitCancellation(tokenSource.Token);
|
||||
}
|
||||
}
|
||||
catch (Exception Except)
|
||||
{
|
||||
@ -195,7 +202,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
});
|
||||
|
||||
Parallel.ForEach(pluginTasks, async (_task) => await _task);
|
||||
await Task.WhenAny(pluginTasks);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@ -314,7 +321,8 @@ namespace IW4MAdmin
|
||||
// this happens for some reason rarely where the client spots get out of order
|
||||
// possible a connect/reconnect game event before we get to process it here
|
||||
// it appears that new games decide to switch client slots between maps (even if the clients aren't disconnecting)
|
||||
else if (existingClient != null && existingClient.ClientNumber != E.Origin.ClientNumber)
|
||||
// bots can have duplicate names which causes conflicting GUIDs
|
||||
else if (existingClient != null && existingClient.ClientNumber != E.Origin.ClientNumber && !E.Origin.IsBot)
|
||||
{
|
||||
Logger.WriteWarning($"client {E.Origin} is trying to connect in client slot {E.Origin.ClientNumber}, but they are already registed in client slot {existingClient.ClientNumber}, swapping...");
|
||||
// we need to remove them so the client spots can swap
|
||||
@ -727,7 +735,7 @@ namespace IW4MAdmin
|
||||
Origin = client
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
|
||||
await e.WaitAsync(Utilities.DefaultCommandTimeout, new CancellationTokenRegistration().Token);
|
||||
}
|
||||
@ -771,10 +779,11 @@ namespace IW4MAdmin
|
||||
{
|
||||
Type = GameEvent.EventType.PreDisconnect,
|
||||
Origin = disconnectingClient,
|
||||
Owner = this
|
||||
Owner = this,
|
||||
Source = GameEvent.EventSource.Status
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
await e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken);
|
||||
}
|
||||
|
||||
@ -793,10 +802,11 @@ namespace IW4MAdmin
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
Origin = client,
|
||||
Owner = this,
|
||||
IsBlocking = true
|
||||
IsBlocking = true,
|
||||
Source = GameEvent.EventSource.Status
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
await e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken);
|
||||
}
|
||||
|
||||
@ -811,7 +821,7 @@ namespace IW4MAdmin
|
||||
Owner = this
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
}
|
||||
|
||||
if (ConnectionErrors > 0)
|
||||
@ -824,7 +834,7 @@ namespace IW4MAdmin
|
||||
Target = Utilities.IW4MAdminClient(this)
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(_event);
|
||||
Manager.AddEvent(_event);
|
||||
}
|
||||
|
||||
ConnectionErrors = 0;
|
||||
@ -846,7 +856,7 @@ namespace IW4MAdmin
|
||||
Data = ConnectionErrors.ToString()
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(_event);
|
||||
Manager.AddEvent(_event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1071,7 +1081,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
LogEvent = new GameLogEventDetection(this, LogPath, ServerConfig.GameLogServerUrl);
|
||||
LogEvent = new GameLogEventDetection(this, GenerateUriForLog(LogPath, ServerConfig.GameLogServerUrl?.AbsoluteUri), gameLogReaderFactory);
|
||||
Logger.WriteInfo($"Log file is {LogPath}");
|
||||
|
||||
_ = Task.Run(() => LogEvent.PollForChanges());
|
||||
@ -1080,6 +1090,21 @@ namespace IW4MAdmin
|
||||
#endif
|
||||
}
|
||||
|
||||
public Uri[] GenerateUriForLog(string logPath, string gameLogServerUrl)
|
||||
{
|
||||
var logUri = new Uri(logPath);
|
||||
|
||||
if (string.IsNullOrEmpty(gameLogServerUrl))
|
||||
{
|
||||
return new[] { logUri };
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return new[] { new Uri(gameLogServerUrl), logUri };
|
||||
}
|
||||
}
|
||||
|
||||
public static string GenerateLogPath(LogPathGeneratorInfo logInfo)
|
||||
{
|
||||
string logPath;
|
||||
@ -1179,7 +1204,7 @@ namespace IW4MAdmin
|
||||
Owner = this
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
|
||||
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7");
|
||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
|
@ -30,7 +30,7 @@ namespace IW4MAdmin.Application
|
||||
/// entrypoint of the application
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task Main()
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", Utilities.OperatingDirectory);
|
||||
|
||||
@ -45,7 +45,7 @@ namespace IW4MAdmin.Application
|
||||
Console.WriteLine($" Version {Utilities.GetVersionAsString()}");
|
||||
Console.WriteLine("=====================================================");
|
||||
|
||||
await LaunchAsync();
|
||||
await LaunchAsync(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -64,7 +64,7 @@ namespace IW4MAdmin.Application
|
||||
/// task that initializes application and starts the application monitoring and runtime tasks
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static async Task LaunchAsync()
|
||||
private static async Task LaunchAsync(string[] args)
|
||||
{
|
||||
restart:
|
||||
ITranslationLookup translationLookup = null;
|
||||
@ -74,7 +74,7 @@ namespace IW4MAdmin.Application
|
||||
ConfigurationMigration.MoveConfigFolder10518(null);
|
||||
ConfigurationMigration.CheckDirectories();
|
||||
|
||||
var services = ConfigureServices();
|
||||
var services = ConfigureServices(args);
|
||||
serviceProvider = services.BuildServiceProvider();
|
||||
ServerManager = (ApplicationManager)serviceProvider.GetRequiredService<IManager>();
|
||||
translationLookup = serviceProvider.GetRequiredService<ITranslationLookup>();
|
||||
@ -252,7 +252,7 @@ namespace IW4MAdmin.Application
|
||||
Owner = ServerManager.Servers[0]
|
||||
};
|
||||
|
||||
ServerManager.GetEventHandler().AddEvent(E);
|
||||
ServerManager.AddEvent(E);
|
||||
await E.WaitAsync(Utilities.DefaultCommandTimeout, ServerManager.CancellationToken);
|
||||
Console.Write('>');
|
||||
}
|
||||
@ -266,7 +266,7 @@ namespace IW4MAdmin.Application
|
||||
/// <summary>
|
||||
/// Configures the dependency injection services
|
||||
/// </summary>
|
||||
private static IServiceCollection ConfigureServices()
|
||||
private static IServiceCollection ConfigureServices(string[] args)
|
||||
{
|
||||
var defaultLogger = new Logger("IW4MAdmin-Manager");
|
||||
var pluginImporter = new PluginImporter(defaultLogger);
|
||||
@ -285,6 +285,7 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IConfigurationHandlerFactory, ConfigurationHandlerFactory>()
|
||||
.AddSingleton<IParserRegexFactory, ParserRegexFactory>()
|
||||
.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>()
|
||||
.AddSingleton<IGameLogReaderFactory, GameLogReaderFactory>()
|
||||
.AddSingleton<IAuditInformationRepository, AuditInformationRepository>()
|
||||
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
||||
.AddSingleton(_serviceProvider =>
|
||||
@ -295,6 +296,15 @@ namespace IW4MAdmin.Application
|
||||
})
|
||||
.AddSingleton<IManager, ApplicationManager>();
|
||||
|
||||
if (args.Contains("serialevents"))
|
||||
{
|
||||
serviceCollection.AddSingleton<IEventHandler, SerialGameEventHandler>();
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceCollection.AddSingleton<IEventHandler, GameEventHandler>();
|
||||
}
|
||||
|
||||
// register the native commands
|
||||
foreach (var commandType in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
|
||||
.Where(_command => _command.BaseType == typeof(Command)))
|
||||
|
27
Application/Misc/EventLog.cs
Normal file
27
Application/Misc/EventLog.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Newtonsoft.Json;
|
||||
using SharedLibraryCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
public class EventLog : Dictionary<long, IList<GameEvent>>
|
||||
{
|
||||
private static JsonSerializerSettings serializationSettings;
|
||||
|
||||
public static JsonSerializerSettings BuildVcrSerializationSettings()
|
||||
{
|
||||
if (serializationSettings == null)
|
||||
{
|
||||
serializationSettings = new JsonSerializerSettings() { Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
|
||||
serializationSettings.Converters.Add(new IPAddressConverter());
|
||||
serializationSettings.Converters.Add(new IPEndPointConverter());
|
||||
serializationSettings.Converters.Add(new GameEventConverter());
|
||||
serializationSettings.Converters.Add(new ClientEntityConverter());
|
||||
}
|
||||
|
||||
return serializationSettings;
|
||||
}
|
||||
}
|
||||
}
|
149
Application/Misc/SerializationHelpers.cs
Normal file
149
Application/Misc/SerializationHelpers.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Net;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
using static SharedLibraryCore.GameEvent;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
class IPAddressConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return (objectType == typeof(IPAddress));
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
return IPAddress.Parse((string)reader.Value);
|
||||
}
|
||||
}
|
||||
|
||||
class IPEndPointConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return (objectType == typeof(IPEndPoint));
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
IPEndPoint ep = (IPEndPoint)value;
|
||||
JObject jo = new JObject();
|
||||
jo.Add("Address", JToken.FromObject(ep.Address, serializer));
|
||||
jo.Add("Port", ep.Port);
|
||||
jo.WriteTo(writer);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
JObject jo = JObject.Load(reader);
|
||||
IPAddress address = jo["Address"].ToObject<IPAddress>(serializer);
|
||||
int port = (int)jo["Port"];
|
||||
return new IPEndPoint(address, port);
|
||||
}
|
||||
}
|
||||
|
||||
class ClientEntityConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType) => objectType == typeof(EFClient);
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType,object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var jsonObject = JObject.Load(reader);
|
||||
|
||||
return new EFClient
|
||||
{
|
||||
NetworkId = (long)jsonObject["NetworkId"],
|
||||
ClientNumber = (int)jsonObject["ClientNumber"],
|
||||
State = Enum.Parse<ClientState>(jsonObject["state"].ToString()),
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
IPAddress = (int?)jsonObject["IPAddress"],
|
||||
Name = jsonObject["Name"].ToString()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var client = value as EFClient;
|
||||
var jsonObject = new JObject
|
||||
{
|
||||
{ "NetworkId", client.NetworkId },
|
||||
{ "ClientNumber", client.ClientNumber },
|
||||
{ "IPAddress", client.CurrentAlias?.IPAddress },
|
||||
{ "Name", client.CurrentAlias?.Name },
|
||||
{ "State", (int)client.State }
|
||||
};
|
||||
|
||||
jsonObject.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
|
||||
class GameEventConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType) =>objectType == typeof(GameEvent);
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var jsonObject = JObject.Load(reader);
|
||||
|
||||
return new GameEvent
|
||||
{
|
||||
Type = Enum.Parse<EventType>(jsonObject["Type"].ToString()),
|
||||
Subtype = jsonObject["Subtype"]?.ToString(),
|
||||
Source = Enum.Parse<EventSource>(jsonObject["Source"].ToString()),
|
||||
RequiredEntity = Enum.Parse<EventRequiredEntity>(jsonObject["RequiredEntity"].ToString()),
|
||||
Data = jsonObject["Data"].ToString(),
|
||||
Message = jsonObject["Message"].ToString(),
|
||||
GameTime = (int?)jsonObject["GameTime"],
|
||||
Origin = jsonObject["Origin"]?.ToObject<EFClient>(serializer),
|
||||
Target = jsonObject["Target"]?.ToObject<EFClient>(serializer),
|
||||
ImpersonationOrigin = jsonObject["ImpersonationOrigin"]?.ToObject<EFClient>(serializer),
|
||||
IsRemote = (bool)jsonObject["IsRemote"],
|
||||
Extra = null, // fix
|
||||
Time = (DateTime)jsonObject["Time"],
|
||||
IsBlocking = (bool)jsonObject["IsBlocking"]
|
||||
};
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var gameEvent = value as GameEvent;
|
||||
|
||||
var jsonObject = new JObject
|
||||
{
|
||||
{ "Type", (int)gameEvent.Type },
|
||||
{ "Subtype", gameEvent.Subtype },
|
||||
{ "Source", (int)gameEvent.Source },
|
||||
{ "RequiredEntity", (int)gameEvent.RequiredEntity },
|
||||
{ "Data", gameEvent.Data },
|
||||
{ "Message", gameEvent.Message },
|
||||
{ "GameTime", gameEvent.GameTime },
|
||||
{ "Origin", gameEvent.Origin != null ? JToken.FromObject(gameEvent.Origin, serializer) : null },
|
||||
{ "Target", gameEvent.Target != null ? JToken.FromObject(gameEvent.Target, serializer) : null },
|
||||
{ "ImpersonationOrigin", gameEvent.ImpersonationOrigin != null ? JToken.FromObject(gameEvent.ImpersonationOrigin, serializer) : null},
|
||||
{ "IsRemote", gameEvent.IsRemote },
|
||||
{ "Extra", gameEvent.Extra?.ToString() },
|
||||
{ "Time", gameEvent.Time },
|
||||
{ "IsBlocking", gameEvent.IsBlocking }
|
||||
};
|
||||
|
||||
jsonObject.WriteTo(writer);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application.RCon
|
||||
@ -193,7 +194,8 @@ namespace IW4MAdmin.Application.RCon
|
||||
throw new ServerException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_NOT_RUNNING"].FormatExt(Endpoint.ToString()));
|
||||
}
|
||||
|
||||
string[] headerSplit = responseString.Split(type == StaticHelpers.QueryType.GET_INFO ? config.CommandPrefixes.RconGetInfoResponseHeader : config.CommandPrefixes.RConResponse);
|
||||
string responseHeaderMatch = Regex.Match(responseString, config.CommandPrefixes.RConResponse).Value;
|
||||
string[] headerSplit = responseString.Split(type == StaticHelpers.QueryType.GET_INFO ? config.CommandPrefixes.RconGetInfoResponseHeader : responseHeaderMatch);
|
||||
|
||||
if (headerSplit.Length != 2)
|
||||
{
|
||||
|
@ -179,9 +179,15 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
}
|
||||
|
||||
long networkId;
|
||||
string name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine();
|
||||
|
||||
try
|
||||
{
|
||||
networkId = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]].ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
string networkIdString = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]];
|
||||
|
||||
networkId = networkIdString.IsBotGuid() ?
|
||||
name.GenerateGuidFromString() :
|
||||
networkIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
||||
}
|
||||
|
||||
catch (FormatException)
|
||||
@ -189,7 +195,6 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
continue;
|
||||
}
|
||||
|
||||
string name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine();
|
||||
int? ip = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]].Split(':')[0].ConvertToIP();
|
||||
|
||||
var client = new EFClient()
|
||||
@ -206,13 +211,13 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
State = EFClient.ClientState.Connecting
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
if (client.NetworkId < 1000 && client.NetworkId > 0)
|
||||
{
|
||||
client.IPAddress = 2147483646;
|
||||
client.Ping = 0;
|
||||
}
|
||||
#endif
|
||||
//#if DEBUG
|
||||
// if (client.NetworkId < 1000 && client.NetworkId > 0)
|
||||
// {
|
||||
// client.IPAddress = 2147483646;
|
||||
// client.Ping = 0;
|
||||
// }
|
||||
//#endif
|
||||
|
||||
StatusPlayers.Add(client);
|
||||
}
|
||||
|
41
Application/SerialGameEventHandler.cs
Normal file
41
Application/SerialGameEventHandler.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
class SerialGameEventHandler : IEventHandler
|
||||
{
|
||||
private delegate void GameEventAddedEventHandler(object sender, GameEventArgs args);
|
||||
private event GameEventAddedEventHandler GameEventAdded;
|
||||
|
||||
private static readonly GameEvent.EventType[] overrideEvents = new[]
|
||||
{
|
||||
GameEvent.EventType.Connect,
|
||||
GameEvent.EventType.Disconnect,
|
||||
GameEvent.EventType.Quit,
|
||||
GameEvent.EventType.Stop
|
||||
};
|
||||
|
||||
public SerialGameEventHandler()
|
||||
{
|
||||
GameEventAdded += GameEventHandler_GameEventAdded;
|
||||
}
|
||||
|
||||
private async void GameEventHandler_GameEventAdded(object sender, GameEventArgs args)
|
||||
{
|
||||
await (sender as IManager).ExecuteEvent(args.Event);
|
||||
EventApi.OnGameEvent(args.Event);
|
||||
}
|
||||
|
||||
public void HandleEvent(IManager manager, GameEvent gameEvent)
|
||||
{
|
||||
if (manager.IsRunning || overrideEvents.Contains(gameEvent.Type))
|
||||
{
|
||||
GameEventAdded?.Invoke(manager, new GameEventArgs(null, false, gameEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.2,
|
||||
version: 0.3,
|
||||
name: 'Plutonium IW5 Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -28,7 +28,7 @@ var plugin = {
|
||||
rconParser.Configuration.CanGenerateLogPath = true;
|
||||
|
||||
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +lastmsg +address +qport +rate *';
|
||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$';
|
||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+|0) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$';
|
||||
rconParser.Configuration.Status.AddMapping(100, 1);
|
||||
rconParser.Configuration.Status.AddMapping(101, 2);
|
||||
rconParser.Configuration.Status.AddMapping(102, 3);
|
||||
|
@ -3,7 +3,7 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax, Xerxes',
|
||||
version: 0.7,
|
||||
version: 0.8,
|
||||
name: 'Plutonium T6 Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -27,7 +27,7 @@ var plugin = {
|
||||
rconParser.Configuration.WaitForResponse = false;
|
||||
|
||||
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +lastmsg +address +qport +rate *';
|
||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$';
|
||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+|0) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$';
|
||||
rconParser.Configuration.Status.AddMapping(100, 1);
|
||||
rconParser.Configuration.Status.AddMapping(101, 2);
|
||||
rconParser.Configuration.Status.AddMapping(102, 3);
|
||||
|
@ -3,7 +3,7 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.3,
|
||||
version: 0.4,
|
||||
name: 'Tekno MW3 Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -14,11 +14,11 @@ var plugin = {
|
||||
rconParser = manager.GenerateDynamicRConParser(this.name);
|
||||
eventParser = manager.GenerateDynamicEventParser(this.name);
|
||||
|
||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[A-Z]|[0-9]){16,32})\t +(.{0,16}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+\\:-?\\d{1,5}|loopback) *$';
|
||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[A-Z]|[0-9]){16,32}|0)\t +(.{0,16}) +([0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+\\:-?\\d{1,5}|loopback) *$';
|
||||
rconParser.Configuration.Status.AddMapping(104, 5); // RConName
|
||||
rconParser.Configuration.Status.AddMapping(103, 4); // RConNetworkId
|
||||
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff';
|
||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff(print)?';
|
||||
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
|
||||
rconParser.Configuration.CommandPrefixes.Say = 'say {0}';
|
||||
rconParser.Configuration.CommandPrefixes.Kick = 'dropclient {0} "{1}"';
|
||||
|
@ -24,6 +24,12 @@ namespace IW4MAdmin.Plugins.Stats.Events
|
||||
return (EVENT_SCRIPTKILL, EVENT_SCRIPTKILL, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) =>
|
||||
{
|
||||
string[] lineSplit = eventLine.Split(";");
|
||||
|
||||
if (lineSplit[1].IsBotGuid() || lineSplit[2].IsBotGuid())
|
||||
{
|
||||
return autoEvent;
|
||||
}
|
||||
|
||||
long originId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle, 1);
|
||||
long targetId = lineSplit[2].ConvertGuidToLong(config.GuidNumberStyle, 1);
|
||||
|
||||
@ -48,6 +54,12 @@ namespace IW4MAdmin.Plugins.Stats.Events
|
||||
return (EVENT_SCRIPTDAMAGE, EVENT_SCRIPTDAMAGE, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) =>
|
||||
{
|
||||
string[] lineSplit = eventLine.Split(";");
|
||||
|
||||
if (lineSplit[1].IsBotGuid() || lineSplit[2].IsBotGuid())
|
||||
{
|
||||
return autoEvent;
|
||||
}
|
||||
|
||||
long originId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle, 1);
|
||||
long targetId = lineSplit[2].ConvertGuidToLong(config.GuidNumberStyle, 1);
|
||||
|
||||
@ -71,6 +83,12 @@ namespace IW4MAdmin.Plugins.Stats.Events
|
||||
return (EVENT_JOINTEAM, EVENT_JOINTEAM, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) =>
|
||||
{
|
||||
string[] lineSplit = eventLine.Split(";");
|
||||
|
||||
if (lineSplit[1].IsBotGuid() || lineSplit[2].IsBotGuid())
|
||||
{
|
||||
return autoEvent;
|
||||
}
|
||||
|
||||
long originId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle, 1);
|
||||
long targetId = lineSplit[2].ConvertGuidToLong(config.GuidNumberStyle, 1);
|
||||
|
||||
|
@ -530,7 +530,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
/// </summary>
|
||||
/// <param name="origin"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsWorldDamage(EFClient origin) => origin?.NetworkId == 1;
|
||||
private bool IsWorldDamage(EFClient origin) => origin?.NetworkId == Utilities.WORLD_ID || origin?.ClientId == Utilities.WORLD_ID;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if we should try to use anticheat even if sv_customcallbacks is not defined
|
||||
|
@ -16,7 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.2.11" PrivateAssets="All" />
|
||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2.2.12" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
@ -72,7 +72,7 @@ namespace Tests
|
||||
}
|
||||
};
|
||||
|
||||
_manager.GetEventHandler().AddEvent(e);
|
||||
_manager.AddEvent(e);
|
||||
e.Complete();
|
||||
|
||||
e = new GameEvent()
|
||||
@ -91,7 +91,7 @@ namespace Tests
|
||||
}
|
||||
};
|
||||
|
||||
_manager.GetEventHandler().AddEvent(e);
|
||||
_manager.AddEvent(e);
|
||||
e.Complete();
|
||||
|
||||
e = new GameEvent()
|
||||
@ -110,7 +110,7 @@ namespace Tests
|
||||
}
|
||||
};
|
||||
|
||||
_manager.GetEventHandler().AddEvent(e);
|
||||
_manager.AddEvent(e);
|
||||
e.Complete();
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace Tests
|
||||
Owner = Manager.GetServers()[0]
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
e.Complete();
|
||||
|
||||
var client = Manager.GetServers()[0].Clients[0];
|
||||
@ -43,7 +43,7 @@ namespace Tests
|
||||
Owner = e.Owner
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
e.Complete();
|
||||
|
||||
Assert.True(client.Warnings == 1, "client wasn't warned for objectional language");
|
||||
|
@ -49,7 +49,7 @@ namespace SharedLibraryCore.Commands
|
||||
Data = cmd,
|
||||
Owner = E.Owner
|
||||
};
|
||||
E.Owner.Manager.GetEventHandler().AddEvent(impersonatedCommandEvent);
|
||||
E.Owner.Manager.AddEvent(impersonatedCommandEvent);
|
||||
|
||||
var result = await impersonatedCommandEvent.WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken);
|
||||
var response = E.Owner.CommandResult.Where(c => c.ClientId == E.Target.ClientId).ToList();
|
||||
|
@ -23,9 +23,9 @@ namespace SharedLibraryCore.Events
|
||||
return eventList;
|
||||
}
|
||||
|
||||
public static void OnGameEvent(object sender, GameEventArgs eventState)
|
||||
public static void OnGameEvent(GameEvent gameEvent)
|
||||
{
|
||||
var E = eventState.Event;
|
||||
var E = gameEvent;
|
||||
// don't want to clog up the api with unknown events
|
||||
if (E.Type == GameEvent.EventType.Unknown)
|
||||
return;
|
||||
|
@ -194,6 +194,14 @@ namespace SharedLibraryCore
|
||||
Target = 4
|
||||
}
|
||||
|
||||
public enum EventSource
|
||||
{
|
||||
Unspecified,
|
||||
Log,
|
||||
Status,
|
||||
Internal
|
||||
}
|
||||
|
||||
static long NextEventId;
|
||||
static long GetNextEventId()
|
||||
{
|
||||
@ -214,6 +222,7 @@ namespace SharedLibraryCore
|
||||
}
|
||||
|
||||
public EventType Type;
|
||||
public EventSource Source { get; set; }
|
||||
/// <summary>
|
||||
/// suptype of the event for more detailed classification
|
||||
/// </summary>
|
||||
@ -229,7 +238,7 @@ namespace SharedLibraryCore
|
||||
public EFClient Target;
|
||||
public EFClient ImpersonationOrigin { get; set; }
|
||||
public Server Owner;
|
||||
public bool IsRemote { get; set; } = false;
|
||||
public bool IsRemote { get; set; }
|
||||
public object Extra { get; set; }
|
||||
private readonly ManualResetEvent _eventFinishedWaiter;
|
||||
public DateTime Time { get; set; }
|
||||
|
@ -1,18 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// This class handle games events (from log, manual events, etc)
|
||||
/// handles games events (from log, manual events, etc)
|
||||
/// </summary>
|
||||
public interface IEventHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Add a game event event to the queue to be processed
|
||||
/// </summary>
|
||||
/// <param name="gameEvent">Game event</param>
|
||||
void AddEvent(GameEvent gameEvent);
|
||||
/// <param name="manager">application manager instance</param>
|
||||
/// <param name="gameEvent">game event</param>
|
||||
void HandleEvent(IManager manager, GameEvent gameEvent);
|
||||
}
|
||||
}
|
||||
|
@ -4,18 +4,17 @@ using System.Threading.Tasks;
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// represents the abtraction of game log reading
|
||||
/// represents the abstraction of game log reading
|
||||
/// </summary>
|
||||
public interface IGameLogReader
|
||||
{
|
||||
/// <summary>
|
||||
/// get new events that have occured since the last poll
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="fileSizeDiff"></param>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <returns></returns>
|
||||
Task<IEnumerable<GameEvent>> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition);
|
||||
Task<IEnumerable<GameEvent>> ReadEventsFromLog(long fileSizeDiff, long startPosition);
|
||||
|
||||
/// <summary>
|
||||
/// how long the log file is
|
||||
|
18
SharedLibraryCore/Interfaces/IGameLogReaderFactory.cs
Normal file
18
SharedLibraryCore/Interfaces/IGameLogReaderFactory.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// factory interface to create game log readers based on the log file uri
|
||||
/// </summary>
|
||||
public interface IGameLogReaderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// generates a new game log reader based on the provided Uri
|
||||
/// </summary>
|
||||
/// <param name="logUris">collection of log uri used to generate the log reader</param>
|
||||
/// <param name="eventParser">event parser for the log reader</param>
|
||||
/// <returns></returns>
|
||||
IGameLogReader CreateGameLogReader(Uri[] logUris, IEventParser eventParser);
|
||||
}
|
||||
}
|
@ -24,11 +24,6 @@ namespace SharedLibraryCore.Interfaces
|
||||
AliasService GetAliasService();
|
||||
PenaltyService GetPenaltyService();
|
||||
/// <summary>
|
||||
/// Get the event handlers
|
||||
/// </summary>
|
||||
/// <returns>EventHandler for the manager</returns>
|
||||
IEventHandler GetEventHandler();
|
||||
/// <summary>
|
||||
/// enumerates the registered plugin instances
|
||||
/// </summary>
|
||||
IEnumerable<IPlugin> Plugins { get; }
|
||||
@ -68,7 +63,12 @@ namespace SharedLibraryCore.Interfaces
|
||||
string ExternalIPAddress { get; }
|
||||
CancellationToken CancellationToken { get; }
|
||||
bool IsRestartRequested { get; }
|
||||
//OnServerEventEventHandler OnServerEvent { get; set; }
|
||||
bool IsRunning { get; }
|
||||
Task ExecuteEvent(GameEvent gameEvent);
|
||||
/// <summary>
|
||||
/// queues an event for processing
|
||||
/// </summary>
|
||||
/// <param name="gameEvent">event to be processed</param>
|
||||
void AddEvent(GameEvent gameEvent);
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
Data = message
|
||||
};
|
||||
|
||||
CurrentServer?.Manager.GetEventHandler().AddEvent(e);
|
||||
CurrentServer?.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
Warnings++;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -202,7 +202,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
|
||||
Warnings = 0;
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -243,7 +243,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
}
|
||||
|
||||
sender.SetAdditionalProperty("_reportCount", reportCount + 1);
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -308,7 +308,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -336,7 +336,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
}
|
||||
|
||||
State = ClientState.Disconnecting;
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -366,7 +366,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
}
|
||||
|
||||
State = ClientState.Disconnecting;
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
}
|
||||
|
||||
State = ClientState.Disconnecting;
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -428,7 +428,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
Level = newPermission;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
sender.CurrentServer.Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -565,7 +565,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
Owner = CurrentServer,
|
||||
};
|
||||
|
||||
CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
CurrentServer.Manager.AddEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,7 +655,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
[NotMapped]
|
||||
public int Score { get; set; }
|
||||
[NotMapped]
|
||||
public bool IsBot => NetworkId == -1;
|
||||
public bool IsBot => NetworkId == Name.GenerateGuidFromString();
|
||||
|
||||
[NotMapped]
|
||||
public ClientState State { get; set; }
|
||||
@ -694,7 +694,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return ((EFClient)obj).NetworkId == this.NetworkId;
|
||||
return obj.GetType() == typeof(EFClient) && ((EFClient)obj).NetworkId == this.NetworkId;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
@ -28,7 +28,7 @@ namespace SharedLibraryCore
|
||||
T7 = 8
|
||||
}
|
||||
|
||||
public Server(IManager mgr, IRConConnectionFactory rconConnectionFactory, ServerConfiguration config)
|
||||
public Server(ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory, IGameLogReaderFactory gameLogReaderFactory)
|
||||
{
|
||||
Password = config.Password;
|
||||
IP = config.IPAddress;
|
||||
@ -46,6 +46,7 @@ namespace SharedLibraryCore
|
||||
NextMessage = 0;
|
||||
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
|
||||
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
|
||||
this.gameLogReaderFactory = gameLogReaderFactory;
|
||||
InitializeTokens();
|
||||
InitializeAutoMessages();
|
||||
}
|
||||
@ -134,7 +135,7 @@ namespace SharedLibraryCore
|
||||
Origin = sender,
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
Manager.AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -296,7 +297,7 @@ namespace SharedLibraryCore
|
||||
{
|
||||
get
|
||||
{
|
||||
return Clients.Where(p => p != null && !p.IsBot).Count();
|
||||
return Clients.Where(p => p != null/* && !p.IsBot*/).Count();
|
||||
}
|
||||
}
|
||||
public int MaxClients { get; protected set; }
|
||||
@ -325,6 +326,7 @@ namespace SharedLibraryCore
|
||||
protected TimeSpan LastMessage;
|
||||
protected DateTime LastPoll;
|
||||
protected ManualResetEventSlim OnRemoteCommandResponse;
|
||||
protected IGameLogReaderFactory gameLogReaderFactory;
|
||||
|
||||
// only here for performance
|
||||
private readonly bool CustomSayEnabled;
|
||||
|
@ -6,7 +6,7 @@
|
||||
<ApplicationIcon />
|
||||
<StartupObject />
|
||||
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
||||
<Version>2.2.11</Version>
|
||||
<Version>2.2.12</Version>
|
||||
<Authors>RaidMax</Authors>
|
||||
<Company>Forever None</Company>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
@ -20,8 +20,8 @@
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<Description>Shared Library for IW4MAdmin</Description>
|
||||
<AssemblyVersion>2.2.11.0</AssemblyVersion>
|
||||
<FileVersion>2.2.11.0</FileVersion>
|
||||
<AssemblyVersion>2.2.12.0</AssemblyVersion>
|
||||
<FileVersion>2.2.12.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
|
||||
|
@ -16,6 +16,7 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
using static SharedLibraryCore.Database.Models.EFPenalty;
|
||||
@ -50,6 +51,10 @@ namespace SharedLibraryCore
|
||||
AdministeredPenalties = new List<EFPenalty>()
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// fallback id for world events
|
||||
/// </summary>
|
||||
public const long WORLD_ID = -1;
|
||||
|
||||
public static string HttpRequest(string location, string header, string headerValue)
|
||||
{
|
||||
@ -295,39 +300,46 @@ namespace SharedLibraryCore
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// converts a string to numerical guid
|
||||
/// </summary>
|
||||
/// <param name="str">source string for guid</param>
|
||||
/// <param name="numberStyle">how to parse the guid</param>
|
||||
/// <param name="fallback">value to use if string is empty</param>
|
||||
/// <returns></returns>
|
||||
public static long ConvertGuidToLong(this string str, NumberStyles numberStyle, long? fallback = null)
|
||||
{
|
||||
str = str.Substring(0, Math.Min(str.Length, 19));
|
||||
var bot = Regex.Match(str, @"bot[0-9]+").Value;
|
||||
var parsableAsNumber = Regex.Match(str, @"([A-F]|[a-f]|[0-9])+").Value;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(str) && fallback.HasValue)
|
||||
{
|
||||
return fallback.Value;
|
||||
}
|
||||
|
||||
long id = 0;
|
||||
|
||||
if (numberStyle == NumberStyles.Integer)
|
||||
long id;
|
||||
if (!string.IsNullOrEmpty(parsableAsNumber))
|
||||
{
|
||||
long.TryParse(str, numberStyle, CultureInfo.InvariantCulture, out id);
|
||||
|
||||
if (id < 0)
|
||||
if (numberStyle == NumberStyles.Integer)
|
||||
{
|
||||
id = (uint)id;
|
||||
long.TryParse(str, numberStyle, CultureInfo.InvariantCulture, out id);
|
||||
|
||||
if (id < 0)
|
||||
{
|
||||
id = (uint)id;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
long.TryParse(str.Length > 16 ? str.Substring(0, 16) : str, numberStyle, CultureInfo.InvariantCulture, out id);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
long.TryParse(str.Length > 16 ? str.Substring(0, 16) : str, numberStyle, CultureInfo.InvariantCulture, out id);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(bot))
|
||||
{
|
||||
id = -1;
|
||||
#if DEBUG
|
||||
id = str.Sum(_c => _c);
|
||||
#endif
|
||||
// this is a special case for when a real guid is not provided, so we generated it from another source
|
||||
id = str.GenerateGuidFromString();
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
@ -338,6 +350,23 @@ namespace SharedLibraryCore
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// determines if the guid provided appears to be a bot guid
|
||||
/// </summary>
|
||||
/// <param name="guid">value of the guid</param>
|
||||
/// <returns>true if is bot guid, otherwise false</returns>
|
||||
public static bool IsBotGuid(this string guid)
|
||||
{
|
||||
return guid.Contains("bot") || guid == "0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// generates a numerical hashcode from a string value
|
||||
/// </summary>
|
||||
/// <param name="value">value string</param>
|
||||
/// <returns></returns>
|
||||
public static long GenerateGuidFromString(this string value) => string.IsNullOrEmpty(value) ? -1 : HashCode.Combine(value.StripColors());
|
||||
|
||||
public static int? ConvertToIP(this string str)
|
||||
{
|
||||
bool success = IPAddress.TryParse(str, out IPAddress ip);
|
||||
@ -900,6 +929,24 @@ namespace SharedLibraryCore
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://www.planetgeek.ch/2016/12/08/async-method-without-cancellation-support-do-it-my-way/
|
||||
/// </summary>
|
||||
public static async Task WithWaitCancellation(this Task task,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Task completedTask = await Task.WhenAny(task, Task.Delay(Timeout.Infinite, cancellationToken));
|
||||
if (completedTask == task)
|
||||
{
|
||||
await task;
|
||||
}
|
||||
else
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
throw new InvalidOperationException("Infinite delay task completed.");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
|
||||
|
||||
/// <summary>
|
||||
|
@ -23,6 +23,9 @@
|
||||
<None Update="Files\GameEvents.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\replay.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\T6Game.log">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
@ -37,12 +37,10 @@ namespace ApplicationTests
|
||||
cmdConfig = new CommandConfiguration();
|
||||
|
||||
serviceProvider = new ServiceCollection()
|
||||
.BuildBase()
|
||||
.BuildBase(new MockEventHandler(true))
|
||||
.BuildServiceProvider();
|
||||
|
||||
mockEventHandler = new MockEventHandler(true);
|
||||
A.CallTo(() => serviceProvider.GetRequiredService<IManager>().GetEventHandler())
|
||||
.Returns(mockEventHandler);
|
||||
mockEventHandler = serviceProvider.GetRequiredService<MockEventHandler>();
|
||||
|
||||
var mgr = serviceProvider.GetRequiredService<IManager>();
|
||||
transLookup = serviceProvider.GetRequiredService<ITranslationLookup>();
|
||||
@ -54,7 +52,9 @@ namespace ApplicationTests
|
||||
new NonImpersonatableCommand(cmdConfig, transLookup)
|
||||
});
|
||||
|
||||
//Utilities.DefaultCommandTimeout = new TimeSpan(0, 0, 2);
|
||||
A.CallTo(() => mgr.AddEvent(A<GameEvent>.Ignored))
|
||||
.Invokes((fakeCall) => mockEventHandler.HandleEvent(mgr, fakeCall.Arguments[0] as GameEvent));
|
||||
|
||||
}
|
||||
|
||||
#region RUNAS
|
||||
|
@ -9,10 +9,22 @@ using SharedLibraryCore.Services;
|
||||
|
||||
namespace ApplicationTests
|
||||
{
|
||||
static class DepedencyInjectionExtensions
|
||||
static class DependencyInjectionExtensions
|
||||
{
|
||||
public static IServiceCollection BuildBase(this IServiceCollection serviceCollection)
|
||||
public static IServiceCollection BuildBase(this IServiceCollection serviceCollection, IEventHandler eventHandler = null)
|
||||
{
|
||||
|
||||
if (eventHandler == null)
|
||||
{
|
||||
eventHandler = new MockEventHandler();
|
||||
serviceCollection.AddSingleton(eventHandler as MockEventHandler);
|
||||
}
|
||||
|
||||
else if (eventHandler is MockEventHandler mockEventHandler)
|
||||
{
|
||||
serviceCollection.AddSingleton(mockEventHandler);
|
||||
}
|
||||
|
||||
var manager = A.Fake<IManager>();
|
||||
var logger = A.Fake<ILogger>();
|
||||
|
||||
@ -27,10 +39,13 @@ namespace ApplicationTests
|
||||
.AddSingleton(A.Fake<ITranslationLookup>())
|
||||
.AddSingleton(A.Fake<IRConParser>())
|
||||
.AddSingleton(A.Fake<IParserRegexFactory>())
|
||||
.AddSingleton(A.Fake<ClientService>());
|
||||
.AddSingleton<DataFileLoader>()
|
||||
.AddSingleton(A.Fake<ClientService>())
|
||||
.AddSingleton(A.Fake<IGameLogReaderFactory>())
|
||||
.AddSingleton(eventHandler);
|
||||
|
||||
serviceCollection.AddSingleton(_sp => new IW4MServer(_sp.GetRequiredService<IManager>(), ConfigurationGenerators.CreateServerConfiguration(),
|
||||
_sp.GetRequiredService<ITranslationLookup>(), _sp.GetRequiredService<IRConConnectionFactory>())
|
||||
_sp.GetRequiredService<ITranslationLookup>(), _sp.GetRequiredService<IRConConnectionFactory>(), _sp.GetRequiredService<IGameLogReaderFactory>())
|
||||
{
|
||||
RconParser = _sp.GetRequiredService<IRConParser>()
|
||||
});
|
16
Tests/ApplicationTests/Fixtures/DataFileLoader.cs
Normal file
16
Tests/ApplicationTests/Fixtures/DataFileLoader.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using IW4MAdmin.Application.Misc;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationTests.Fixtures
|
||||
{
|
||||
class DataFileLoader
|
||||
{
|
||||
public async Task<T> Load<T>(string fileName)
|
||||
{
|
||||
string data = await File.ReadAllTextAsync($"{fileName}.json");
|
||||
return JsonConvert.DeserializeObject<T>(data, EventLog.BuildVcrSerializationSettings());
|
||||
}
|
||||
}
|
||||
}
|
17
Tests/ApplicationTests/GameEventHandlerTests.cs
Normal file
17
Tests/ApplicationTests/GameEventHandlerTests.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using IW4MAdmin;
|
||||
using IW4MAdmin.Application;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using SharedLibraryCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class GameEventHandlerTests
|
||||
{
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
using FakeItEasy;
|
||||
using IW4MAdmin;
|
||||
using IW4MAdmin.Application.IO;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
@ -12,11 +14,24 @@ namespace ApplicationTests
|
||||
public class IOTests
|
||||
{
|
||||
|
||||
private IServiceProvider serviceProvider;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
serviceProvider = new ServiceCollection().BuildBase().BuildServiceProvider();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task GameLogEventDetection_WorksAfterFileSizeReset()
|
||||
{
|
||||
var reader = A.Fake<IGameLogReader>();
|
||||
var detect = new GameLogEventDetection(null, "", A.Fake<Uri>(), reader);
|
||||
var factory = A.Fake<IGameLogReaderFactory>();
|
||||
|
||||
A.CallTo(() => factory.CreateGameLogReader(A<Uri[]>.Ignored, A<IEventParser>.Ignored))
|
||||
.Returns(reader);
|
||||
|
||||
var detect = new GameLogEventDetection(serviceProvider.GetService<IW4MServer>(), new Uri[] { new Uri("C:\\test.log") }, factory);
|
||||
|
||||
A.CallTo(() => reader.Length)
|
||||
.Returns(100)
|
||||
@ -35,7 +50,7 @@ namespace ApplicationTests
|
||||
await detect.UpdateLogEvents();
|
||||
}
|
||||
|
||||
A.CallTo(() => reader.ReadEventsFromLog(A<Server>.Ignored, A<long>.Ignored, A<long>.Ignored))
|
||||
A.CallTo(() => reader.ReadEventsFromLog(A<long>.Ignored, A<long>.Ignored))
|
||||
.MustHaveHappenedTwiceExactly();
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ namespace ApplicationTests
|
||||
fakeManager = serviceProvider.GetRequiredService<IManager>();
|
||||
fakeRConConnection = serviceProvider.GetRequiredService<IRConConnection>();
|
||||
fakeRConParser = serviceProvider.GetRequiredService<IRConParser>();
|
||||
mockEventHandler = serviceProvider.GetRequiredService<MockEventHandler>();
|
||||
|
||||
var rconConnectionFactory = serviceProvider.GetRequiredService<IRConConnectionFactory>();
|
||||
|
||||
@ -45,10 +46,9 @@ namespace ApplicationTests
|
||||
A.CallTo(() => fakeRConParser.Configuration)
|
||||
.Returns(ConfigurationGenerators.CreateRConParserConfiguration(serviceProvider.GetRequiredService<IParserRegexFactory>()));
|
||||
|
||||
|
||||
mockEventHandler = new MockEventHandler();
|
||||
A.CallTo(() => fakeManager.GetEventHandler())
|
||||
.Returns(mockEventHandler);
|
||||
A.CallTo(() => fakeManager.AddEvent(A<GameEvent>.Ignored))
|
||||
.Invokes((fakeCall) => mockEventHandler.HandleEvent(fakeManager, fakeCall.Arguments[0] as GameEvent));
|
||||
|
||||
}
|
||||
|
||||
#region LOG
|
||||
|
@ -14,7 +14,7 @@ namespace ApplicationTests.Mocks
|
||||
_autoExecute = autoExecute;
|
||||
}
|
||||
|
||||
public void AddEvent(GameEvent gameEvent)
|
||||
public void HandleEvent(IManager manager, GameEvent gameEvent)
|
||||
{
|
||||
Events.Add(gameEvent);
|
||||
|
||||
|
21
Tests/ApplicationTests/Mocks/VcrEventReader.cs
Normal file
21
Tests/ApplicationTests/Mocks/VcrEventReader.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationTests.Mocks
|
||||
{
|
||||
class VcrEventReader : IGameLogReader
|
||||
{
|
||||
public long Length => throw new NotImplementedException();
|
||||
|
||||
public int UpdateInterval => throw new NotImplementedException();
|
||||
|
||||
public Task<IEnumerable<GameEvent>> ReadEventsFromLog(long fileSizeDiff, long startPosition)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -31,9 +31,7 @@ namespace ApplicationTests
|
||||
{
|
||||
serviceProvider = new ServiceCollection().BuildBase().BuildServiceProvider();
|
||||
fakeManager = serviceProvider.GetRequiredService<IManager>();
|
||||
mockEventHandler = new MockEventHandler();
|
||||
A.CallTo(() => fakeManager.GetEventHandler())
|
||||
.Returns(mockEventHandler);
|
||||
mockEventHandler = serviceProvider.GetRequiredService<MockEventHandler>();
|
||||
|
||||
var rconConnectionFactory = serviceProvider.GetRequiredService<IRConConnectionFactory>();
|
||||
|
||||
@ -42,6 +40,9 @@ namespace ApplicationTests
|
||||
|
||||
A.CallTo(() => serviceProvider.GetRequiredService<IRConParser>().Configuration)
|
||||
.Returns(ConfigurationGenerators.CreateRConParserConfiguration(serviceProvider.GetRequiredService<IParserRegexFactory>()));
|
||||
|
||||
A.CallTo(() => fakeManager.AddEvent(A<GameEvent>.Ignored))
|
||||
.Invokes((fakeCall) => mockEventHandler.HandleEvent(fakeManager, fakeCall.Arguments[0] as GameEvent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -33,7 +33,7 @@ namespace ApplicationTests
|
||||
var mgr = A.Fake<IManager>();
|
||||
var server = new IW4MServer(mgr,
|
||||
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>());
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
|
||||
|
||||
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>());
|
||||
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
|
||||
@ -59,7 +59,7 @@ namespace ApplicationTests
|
||||
|
||||
var server = new IW4MServer(mgr,
|
||||
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>());
|
||||
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
|
||||
|
||||
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>());
|
||||
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
|
||||
|
@ -63,7 +63,7 @@ namespace ApplicationTests
|
||||
var server = new IW4MServer(mgr,
|
||||
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
|
||||
A.Fake<ITranslationLookup>(),
|
||||
A.Fake<IRConConnectionFactory>());
|
||||
A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
|
||||
|
||||
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>());
|
||||
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
|
||||
|
43
Tests/ApplicationTests/VcrTests.cs
Normal file
43
Tests/ApplicationTests/VcrTests.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using ApplicationTests.Fixtures;
|
||||
using IW4MAdmin;
|
||||
using IW4MAdmin.Application;
|
||||
using IW4MAdmin.Application.Misc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class VcrTests
|
||||
{
|
||||
private IServiceProvider serviceProvider;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
serviceProvider = new ServiceCollection().BuildBase()
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("replay")]
|
||||
public async Task ReplayEvents(string source)
|
||||
{
|
||||
var sourceData = await serviceProvider
|
||||
.GetRequiredService<DataFileLoader>()
|
||||
.Load<EventLog>(source);
|
||||
|
||||
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||
|
||||
foreach (var gameEvent in sourceData.Values.First())
|
||||
{
|
||||
await server.ExecuteEvent(gameEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ namespace WebfrontCore.Controllers
|
||||
IsRemote = true
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(remoteEvent);
|
||||
Manager.AddEvent(remoteEvent);
|
||||
List<CommandResponseInfo> response = null;
|
||||
|
||||
try
|
||||
|
Loading…
Reference in New Issue
Block a user