mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-07 21:58:06 -05:00
The conditional statement in the event parsing logic was incorrect, causing all events to be parsed regardless of the ignore flag. This change corrects the logic to ensure that only non-ignored events are parsed.
738 lines
32 KiB
C#
738 lines
32 KiB
C#
using SharedLibraryCore;
|
|
using SharedLibraryCore.Configuration;
|
|
using SharedLibraryCore.Database.Models;
|
|
using SharedLibraryCore.Interfaces;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Data.Models;
|
|
using IW4MAdmin.Plugins.Stats.Events;
|
|
using Microsoft.Extensions.Logging;
|
|
using SharedLibraryCore.Events.Game;
|
|
using SharedLibraryCore.Interfaces.Events;
|
|
using static System.Int32;
|
|
using static SharedLibraryCore.Server;
|
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
|
|
|
namespace IW4MAdmin.Application.EventParsers
|
|
{
|
|
public class BaseEventParser : IEventParser
|
|
{
|
|
private readonly Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>
|
|
_customEventRegistrations;
|
|
|
|
private readonly ILogger _logger;
|
|
private readonly ApplicationConfiguration _appConfig;
|
|
private readonly IGameScriptEventFactory _gameScriptEventFactory;
|
|
private readonly Dictionary<ParserRegex, GameEvent.EventType> _regexMap;
|
|
private readonly Dictionary<string, GameEvent.EventType> _eventTypeMap;
|
|
|
|
public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger,
|
|
ApplicationConfiguration appConfig, IGameScriptEventFactory gameScriptEventFactory)
|
|
{
|
|
_customEventRegistrations =
|
|
new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>();
|
|
_logger = logger;
|
|
_appConfig = appConfig;
|
|
_gameScriptEventFactory = gameScriptEventFactory;
|
|
|
|
Configuration = new DynamicEventParserConfiguration(parserRegexFactory)
|
|
{
|
|
GameDirectory = "main",
|
|
LocalizeText = "\x15",
|
|
};
|
|
|
|
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);([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);([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.JoinTeam.Pattern = @"^(JT);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);([0-9]+);(\w+);(.+)$";
|
|
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.EventType, 1);
|
|
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
|
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
|
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginTeam, 4);
|
|
Configuration.JoinTeam.AddMapping(ParserRegex.GroupType.OriginName, 5);
|
|
|
|
Configuration.Damage.Pattern =
|
|
@"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none|team\d+)?;([^;]{1,32});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none|team\d+)?;([^;]{1,32})?;((?:[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);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetTeam, 4);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetName, 5);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginNetworkId, 6);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginClientNumber, 7);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginTeam, 8);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.OriginName, 9);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.Weapon, 10);
|
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.Damage, 11);
|
|
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);(-?[0-9]+);(axis|allies|world|none|team\d+)?;([^;]{1,32});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none|team+\d)?;([^;]{1,32})?;((?:[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);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetTeam, 4);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetName, 5);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginNetworkId, 6);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginClientNumber, 7);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginTeam, 8);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.OriginName, 9);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.Weapon, 10);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.Damage, 11);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
|
|
|
Configuration.MapChange.Pattern = @".*InitGame.*";
|
|
Configuration.MapEnd.Pattern = @".*(?:ExitLevel|ShutdownGame).*";
|
|
|
|
Configuration.Time.Pattern = @"^ *(([0-9]+):([0-9]+) |^[0-9]+ )";
|
|
|
|
_regexMap = new Dictionary<ParserRegex, GameEvent.EventType>
|
|
{
|
|
{ Configuration.Say, GameEvent.EventType.Say },
|
|
{ Configuration.Kill, GameEvent.EventType.Kill },
|
|
{ Configuration.MapChange, GameEvent.EventType.MapChange },
|
|
{ Configuration.MapEnd, GameEvent.EventType.MapEnd },
|
|
{ Configuration.JoinTeam, GameEvent.EventType.JoinTeam }
|
|
};
|
|
|
|
_eventTypeMap = new Dictionary<string, GameEvent.EventType>
|
|
{
|
|
{ "say", GameEvent.EventType.Say },
|
|
{ "sayteam", GameEvent.EventType.SayTeam },
|
|
{ "chat", GameEvent.EventType.Say },
|
|
{ "chatteam", GameEvent.EventType.SayTeam },
|
|
{ "K", GameEvent.EventType.Kill },
|
|
{ "D", GameEvent.EventType.Damage },
|
|
{ "J", GameEvent.EventType.PreConnect },
|
|
{ "JT", GameEvent.EventType.JoinTeam },
|
|
{ "Q", GameEvent.EventType.PreDisconnect }
|
|
};
|
|
}
|
|
|
|
public IEventParserConfiguration Configuration { get; set; }
|
|
|
|
public string Version { get; set; } = "CoD";
|
|
|
|
public Game GameName { get; set; } = Game.COD;
|
|
|
|
public string URLProtocolFormat { get; set; } = "CoD://{{ip}}:{{port}}";
|
|
|
|
public string Name { get; set; } = "Call of Duty";
|
|
|
|
public virtual GameEvent GenerateGameEvent(string logLine)
|
|
{
|
|
var timeMatch = Configuration.Time.PatternMatcher.Match(logLine);
|
|
var gameTime = 0L;
|
|
|
|
if (timeMatch.Success)
|
|
{
|
|
if (timeMatch.Values[0].Contains(':'))
|
|
{
|
|
gameTime = timeMatch
|
|
.Values
|
|
.Skip(2)
|
|
// this converts the timestamp into seconds passed
|
|
.Select((value, index) => long.Parse(value.ToString()) * (index == 0 ? 60 : 1))
|
|
.Sum();
|
|
}
|
|
else
|
|
{
|
|
gameTime = long.Parse(timeMatch.Values[0]);
|
|
}
|
|
|
|
// we want to strip the time from the log line
|
|
logLine = logLine[timeMatch.Values.First().Length..].Trim();
|
|
}
|
|
|
|
var (eventType, eventKey) = GetEventTypeFromLine(logLine);
|
|
|
|
switch (eventType)
|
|
{
|
|
case GameEvent.EventType.Say or GameEvent.EventType.SayTeam:
|
|
return ParseMessageEvent(logLine, gameTime, eventType) ?? GenerateDefaultEvent(logLine, gameTime);
|
|
case GameEvent.EventType.Kill:
|
|
return ParseKillEvent(logLine, gameTime) ?? GenerateDefaultEvent(logLine, gameTime);
|
|
case GameEvent.EventType.Damage:
|
|
return ParseDamageEvent(logLine, gameTime) ?? GenerateDefaultEvent(logLine, gameTime);
|
|
case GameEvent.EventType.PreConnect:
|
|
return ParseClientEnterMatchEvent(logLine, gameTime) ?? GenerateDefaultEvent(logLine, gameTime);
|
|
case GameEvent.EventType.JoinTeam:
|
|
return ParseJoinTeamEvent(logLine, gameTime) ?? GenerateDefaultEvent(logLine, gameTime);
|
|
case GameEvent.EventType.PreDisconnect:
|
|
return ParseClientExitMatchEvent(logLine, gameTime) ?? GenerateDefaultEvent(logLine, gameTime);
|
|
case GameEvent.EventType.MapEnd:
|
|
return ParseMatchEndEvent(logLine, gameTime);
|
|
case GameEvent.EventType.MapChange:
|
|
return ParseMatchStartEvent(logLine, gameTime);
|
|
}
|
|
|
|
if (logLine.StartsWith("GSE;"))
|
|
{
|
|
var gscEvent = new GameScriptEvent
|
|
{
|
|
ScriptData = logLine,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log
|
|
};
|
|
return gscEvent;
|
|
}
|
|
|
|
var split = logLine.Split(";", StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if (split.Length > 1)
|
|
{
|
|
var createdEvent = _gameScriptEventFactory.Create(split[0], logLine.Replace(split[0], ""));
|
|
if (createdEvent is not null)
|
|
{
|
|
var eventTypeName = createdEvent.GetType().Name;
|
|
var isParseIgnoredEvent = eventTypeName is nameof(GameScriptEvent) or nameof(AntiCheatDamageEvent);
|
|
|
|
// avoid parsing base script event (which has no viable properties)
|
|
// and anticheat events as they are manually mapped.
|
|
// for performance as dynamic "Invoke" is relatively costly due
|
|
if (!isParseIgnoredEvent)
|
|
{
|
|
createdEvent.ParseArguments();
|
|
}
|
|
|
|
return createdEvent as GameEventV2;
|
|
}
|
|
}
|
|
|
|
|
|
if (eventKey is null || !_customEventRegistrations.ContainsKey(eventKey))
|
|
{
|
|
return GenerateDefaultEvent(logLine, gameTime);
|
|
}
|
|
|
|
var eventModifier = _customEventRegistrations[eventKey];
|
|
|
|
try
|
|
{
|
|
return eventModifier.Item2(logLine, Configuration, new GameEvent()
|
|
{
|
|
Type = GameEvent.EventType.Other,
|
|
Data = logLine,
|
|
Subtype = eventModifier.Item1,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log
|
|
});
|
|
}
|
|
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Could not handle custom log event generation");
|
|
}
|
|
|
|
return GenerateDefaultEvent(logLine, gameTime);
|
|
}
|
|
|
|
private static GameLogEvent GenerateDefaultEvent(string logLine, long gameTime)
|
|
{
|
|
return new GameLogEvent
|
|
{
|
|
Type = GameEvent.EventType.Unknown,
|
|
Data = logLine,
|
|
LogLine = logLine,
|
|
Origin = Utilities.IW4MAdminClient(),
|
|
Target = Utilities.IW4MAdminClient(),
|
|
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log
|
|
};
|
|
}
|
|
|
|
private static GameEvent ParseMatchStartEvent(string logLine, long gameTime)
|
|
{
|
|
var dump = logLine.Replace("InitGame: ", "").DictionaryFromKeyValue();
|
|
|
|
return new MatchStartEvent
|
|
{
|
|
Type = GameEvent.EventType.MapChange,
|
|
Data = logLine,
|
|
Origin = Utilities.IW4MAdminClient(),
|
|
Target = Utilities.IW4MAdminClient(),
|
|
Extra = dump,
|
|
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
SessionData = dump
|
|
};
|
|
}
|
|
|
|
private static GameEvent ParseMatchEndEvent(string logLine, long gameTime)
|
|
{
|
|
return new MatchEndEvent
|
|
{
|
|
Type = GameEvent.EventType.MapEnd,
|
|
Data = logLine,
|
|
Origin = Utilities.IW4MAdminClient(),
|
|
Target = Utilities.IW4MAdminClient(),
|
|
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
SessionData = logLine
|
|
};
|
|
}
|
|
|
|
private GameEvent ParseClientExitMatchEvent(string logLine, long gameTime)
|
|
{
|
|
var match = Configuration.Quit.PatternMatcher.Match(logLine);
|
|
|
|
if (!match.Success)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originIdString =
|
|
match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
|
var originName = match.Values[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]]
|
|
?.TrimNewLine();
|
|
var originClientNumber =
|
|
Convert.ToInt32(
|
|
match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
|
|
|
var networkId = originIdString.IsBotGuid()
|
|
? originName.GenerateGuidFromString()
|
|
: originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
|
|
|
return new ClientExitMatchEvent
|
|
{
|
|
Type = GameEvent.EventType.PreDisconnect,
|
|
Data = logLine,
|
|
Origin = new EFClient
|
|
{
|
|
CurrentAlias = new EFAlias
|
|
{
|
|
Name = originName
|
|
},
|
|
NetworkId = networkId,
|
|
ClientNumber = originClientNumber,
|
|
State = EFClient.ClientState.Disconnecting
|
|
},
|
|
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
|
IsBlocking = true,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = originClientNumber
|
|
};
|
|
}
|
|
|
|
private GameEvent ParseJoinTeamEvent(string logLine, long gameTime)
|
|
{
|
|
var match = Configuration.JoinTeam.PatternMatcher.Match(logLine);
|
|
|
|
if (!match.Success)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originIdString =
|
|
match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
|
var originName = match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginName]]
|
|
?.TrimNewLine();
|
|
var team = match.Values[Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginTeam]];
|
|
var clientSlotNumber =
|
|
Parse(match.Values[
|
|
Configuration.JoinTeam.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
|
|
|
if (Configuration.TeamMapping.ContainsKey(team))
|
|
{
|
|
team = Configuration.TeamMapping[team].ToString();
|
|
}
|
|
|
|
var networkId = originIdString.IsBotGuid()
|
|
? originName.GenerateGuidFromString()
|
|
: originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
|
|
|
return new ClientJoinTeamEvent
|
|
{
|
|
Type = GameEvent.EventType.JoinTeam,
|
|
Data = logLine,
|
|
Origin = new EFClient
|
|
{
|
|
CurrentAlias = new EFAlias
|
|
{
|
|
Name = originName
|
|
},
|
|
NetworkId = networkId,
|
|
ClientNumber = clientSlotNumber,
|
|
State = EFClient.ClientState.Connected,
|
|
},
|
|
Extra = team,
|
|
RequiredEntity = GameEvent.EventRequiredEntity.Origin,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
TeamName = team,
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = clientSlotNumber
|
|
};
|
|
}
|
|
|
|
private GameEvent ParseClientEnterMatchEvent(string logLine, long gameTime)
|
|
{
|
|
var match = Configuration.Join.PatternMatcher.Match(logLine);
|
|
|
|
if (!match.Success)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originIdString = match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
|
var originName = match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]]
|
|
.TrimNewLine();
|
|
var originClientNumber =
|
|
Convert.ToInt32(
|
|
match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
|
|
|
var networkId = originIdString.IsBotGuid()
|
|
? originName.GenerateGuidFromString()
|
|
: originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
|
|
|
return new ClientEnterMatchEvent
|
|
{
|
|
Type = GameEvent.EventType.PreConnect,
|
|
Data = logLine,
|
|
Origin = new EFClient
|
|
{
|
|
CurrentAlias = new EFAlias
|
|
{
|
|
Name = originName
|
|
},
|
|
NetworkId = networkId,
|
|
ClientNumber = originClientNumber,
|
|
State = EFClient.ClientState.Connecting,
|
|
},
|
|
Extra = originIdString,
|
|
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
|
IsBlocking = true,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = originClientNumber
|
|
};
|
|
}
|
|
|
|
#region DAMAGE
|
|
|
|
private GameEvent ParseDamageEvent(string logLine, long gameTime)
|
|
{
|
|
var match = Configuration.Damage.PatternMatcher.Match(logLine);
|
|
|
|
if (!match.Success)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originIdString = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
|
var targetIdString = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]];
|
|
|
|
var originName = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginName]]
|
|
?.TrimNewLine();
|
|
var targetName = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetName]]
|
|
?.TrimNewLine();
|
|
|
|
var originId = originIdString.IsBotGuid()
|
|
? originName.GenerateGuidFromString()
|
|
: originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
|
var targetId = targetIdString.IsBotGuid()
|
|
? targetName.GenerateGuidFromString()
|
|
: targetIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
|
|
|
var originClientNumber =
|
|
Parse(match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
|
var targetClientNumber =
|
|
Parse(match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetClientNumber]]);
|
|
|
|
var originTeamName =
|
|
match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginTeam]];
|
|
var targetTeamName =
|
|
match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetTeam]];
|
|
|
|
if (Configuration.TeamMapping.ContainsKey(originTeamName))
|
|
{
|
|
originTeamName = Configuration.TeamMapping[originTeamName].ToString();
|
|
}
|
|
|
|
if (Configuration.TeamMapping.ContainsKey(targetTeamName))
|
|
{
|
|
targetTeamName = Configuration.TeamMapping[targetTeamName].ToString();
|
|
}
|
|
|
|
var weaponName = match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.Weapon]];
|
|
TryParse(match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.Damage]],
|
|
out var damage);
|
|
var meansOfDeath =
|
|
match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.MeansOfDeath]];
|
|
var hitLocation =
|
|
match.Values[Configuration.Damage.GroupMapping[ParserRegex.GroupType.HitLocation]];
|
|
|
|
return new ClientDamageEvent
|
|
{
|
|
Type = GameEvent.EventType.Damage,
|
|
Data = logLine,
|
|
Origin = new EFClient { NetworkId = originId, ClientNumber = originClientNumber },
|
|
Target = new EFClient { NetworkId = targetId, ClientNumber = targetClientNumber },
|
|
RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = originClientNumber,
|
|
AttackerTeamName = originTeamName,
|
|
VictimClientName = targetName,
|
|
VictimNetworkId = targetIdString,
|
|
VictimClientSlotNumber = targetClientNumber,
|
|
VictimTeamName = targetTeamName,
|
|
WeaponName = weaponName,
|
|
Damage = damage,
|
|
MeansOfDeath = meansOfDeath,
|
|
HitLocation = hitLocation
|
|
};
|
|
}
|
|
|
|
private GameEvent ParseKillEvent(string logLine, long gameTime)
|
|
{
|
|
var match = Configuration.Kill.PatternMatcher.Match(logLine);
|
|
|
|
if (!match.Success)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originIdString = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
|
var targetIdString = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]];
|
|
|
|
var originName = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginName]]
|
|
?.TrimNewLine();
|
|
var targetName = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetName]]
|
|
?.TrimNewLine();
|
|
|
|
var originId = originIdString.IsBotGuid()
|
|
? originName.GenerateGuidFromString()
|
|
: originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
|
var targetId = targetIdString.IsBotGuid()
|
|
? targetName.GenerateGuidFromString()
|
|
: targetIdString.ConvertGuidToLong(Configuration.GuidNumberStyle, Utilities.WORLD_ID);
|
|
|
|
var originClientNumber =
|
|
Parse(match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
|
var targetClientNumber =
|
|
Parse(match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetClientNumber]]);
|
|
|
|
var originTeamName =
|
|
match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginTeam]];
|
|
var targetTeamName =
|
|
match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetTeam]];
|
|
|
|
if (Configuration.TeamMapping.ContainsKey(originTeamName))
|
|
{
|
|
originTeamName = Configuration.TeamMapping[originTeamName].ToString();
|
|
}
|
|
|
|
if (Configuration.TeamMapping.ContainsKey(targetTeamName))
|
|
{
|
|
targetTeamName = Configuration.TeamMapping[targetTeamName].ToString();
|
|
}
|
|
|
|
var weaponName = match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.Weapon]];
|
|
TryParse(match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.Damage]],
|
|
out var damage);
|
|
var meansOfDeath =
|
|
match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.MeansOfDeath]];
|
|
var hitLocation =
|
|
match.Values[Configuration.Kill.GroupMapping[ParserRegex.GroupType.HitLocation]];
|
|
|
|
return new ClientKillEvent
|
|
{
|
|
Type = GameEvent.EventType.Kill,
|
|
Data = logLine,
|
|
Origin = new EFClient { NetworkId = originId, ClientNumber = originClientNumber },
|
|
Target = new EFClient { NetworkId = targetId, ClientNumber = targetClientNumber },
|
|
RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
// V2
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = originClientNumber,
|
|
AttackerTeamName = originTeamName,
|
|
VictimClientName = targetName,
|
|
VictimNetworkId = targetIdString,
|
|
VictimClientSlotNumber = targetClientNumber,
|
|
VictimTeamName = targetTeamName,
|
|
WeaponName = weaponName,
|
|
Damage = damage,
|
|
MeansOfDeath = meansOfDeath,
|
|
HitLocation = hitLocation
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region MESSAGE
|
|
|
|
private GameEvent ParseMessageEvent(string logLine, long gameTime, GameEvent.EventType eventType)
|
|
{
|
|
var matchResult = Configuration.Say.PatternMatcher.Match(logLine);
|
|
|
|
if (!matchResult.Success)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var message = new string(matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.Message]]
|
|
.Where(c => !char.IsControl(c)).ToArray());
|
|
|
|
if (message.StartsWith("/"))
|
|
{
|
|
message = message[1..];
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(message))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var originIdString =
|
|
matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginNetworkId]];
|
|
var originName = matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginName]]
|
|
?.TrimNewLine();
|
|
var clientNumber =
|
|
Parse(matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
|
|
|
|
var originId = originIdString.IsBotGuid()
|
|
? originName.GenerateGuidFromString()
|
|
: originIdString.ConvertGuidToLong(Configuration.GuidNumberStyle);
|
|
|
|
|
|
if (message.StartsWith(_appConfig.CommandPrefix) || message.StartsWith(_appConfig.BroadcastCommandPrefix))
|
|
{
|
|
return new ClientCommandEvent
|
|
{
|
|
Type = GameEvent.EventType.Command,
|
|
Data = message,
|
|
Origin = new EFClient { NetworkId = originId, ClientNumber = clientNumber },
|
|
Message = message,
|
|
Extra = logLine,
|
|
RequiredEntity = GameEvent.EventRequiredEntity.Origin,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
//V2
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = clientNumber,
|
|
IsTeamMessage = eventType == GameEvent.EventType.SayTeam
|
|
};
|
|
}
|
|
|
|
return new ClientMessageEvent
|
|
{
|
|
Type = GameEvent.EventType.Say,
|
|
Data = message,
|
|
Origin = new EFClient { NetworkId = originId, ClientNumber = clientNumber },
|
|
Message = message,
|
|
Extra = logLine,
|
|
RequiredEntity = GameEvent.EventRequiredEntity.Origin,
|
|
GameTime = gameTime,
|
|
Source = GameEvent.EventSource.Log,
|
|
|
|
//V2
|
|
ClientName = originName,
|
|
ClientNetworkId = originIdString,
|
|
ClientSlotNumber = clientNumber,
|
|
IsTeamMessage = eventType == GameEvent.EventType.SayTeam
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
|
|
private (GameEvent.EventType type, string eventKey) GetEventTypeFromLine(string logLine)
|
|
{
|
|
var lineSplit = logLine.Split(';');
|
|
if (lineSplit.Length > 1)
|
|
{
|
|
var type = lineSplit[0];
|
|
return _eventTypeMap.ContainsKey(type)
|
|
? (_eventTypeMap[type], type)
|
|
: (GameEvent.EventType.Unknown, lineSplit[0]);
|
|
}
|
|
|
|
foreach (var (key, value) in _regexMap)
|
|
{
|
|
var result = key.PatternMatcher.Match(logLine);
|
|
if (result.Success)
|
|
{
|
|
return (value, null);
|
|
}
|
|
}
|
|
|
|
return (GameEvent.EventType.Unknown, null);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void RegisterCustomEvent(string eventSubtype, string eventTriggerValue,
|
|
Func<string, IEventParserConfiguration, GameEvent, GameEvent> eventModifier)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(eventSubtype))
|
|
{
|
|
throw new ArgumentException("Event subtype cannot be empty");
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(eventTriggerValue))
|
|
{
|
|
throw new ArgumentException("Event trigger value cannot be empty");
|
|
}
|
|
|
|
if (eventModifier == null)
|
|
{
|
|
throw new ArgumentException("Event modifier must be specified");
|
|
}
|
|
|
|
if (_customEventRegistrations.ContainsKey(eventTriggerValue))
|
|
{
|
|
throw new ArgumentException($"Event trigger value '{eventTriggerValue}' is already registered");
|
|
}
|
|
|
|
_customEventRegistrations.Add(eventTriggerValue, (eventSubtype, eventModifier));
|
|
}
|
|
}
|
|
}
|