1
0
mirror of https://github.com/RaidMax/IW4M-Admin.git synced 2025-06-07 21:58:06 -05:00

implement functionality to dynamically populate property values from events that inherit from GameScriptEvent

This commit is contained in:
RaidMax 2024-06-22 17:02:04 -05:00
parent dffcae8344
commit 1596af1548
15 changed files with 232 additions and 75 deletions

View File

@ -105,7 +105,7 @@ namespace IW4MAdmin.Application
ConfigHandler = appConfigHandler;
StartTime = DateTime.UtcNow;
PageList = new PageList();
AdditionalEventParsers = new List<IEventParser> { new BaseEventParser(parserRegexFactory, logger, _appConfig) };
AdditionalEventParsers = new List<IEventParser> { new BaseEventParser(parserRegexFactory, logger, _appConfig, serviceProvider.GetRequiredService<IGameScriptEventFactory>()) };
AdditionalRConParsers = new List<IRConParser> { new BaseRConParser(serviceProvider.GetRequiredService<ILogger<BaseRConParser>>(), parserRegexFactory) };
TokenAuthenticator = new TokenAuthentication();
_logger = logger;
@ -710,7 +710,7 @@ namespace IW4MAdmin.Application
public IEventParser GenerateDynamicEventParser(string name)
{
return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration())
return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration(), _serviceProvider.GetRequiredService<IGameScriptEventFactory>())
{
Name = name
};

View File

@ -8,6 +8,7 @@ using System.Linq;
using Data.Models;
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;
@ -21,16 +22,18 @@ namespace IW4MAdmin.Application.EventParsers
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)
ApplicationConfiguration appConfig, IGameScriptEventFactory gameScriptEventFactory)
{
_customEventRegistrations =
new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>();
_logger = logger;
_appConfig = appConfig;
_gameScriptEventFactory = gameScriptEventFactory;
Configuration = new DynamicEventParserConfiguration(parserRegexFactory)
{
@ -183,14 +186,28 @@ namespace IW4MAdmin.Application.EventParsers
if (logLine.StartsWith("GSE;"))
{
return new GameScriptEvent
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)
{
createdEvent.ParseArguments();
return createdEvent as GameEventV2;
}
}
if (eventKey is null || !_customEventRegistrations.ContainsKey(eventKey))
{
return GenerateDefaultEvent(logLine, gameTime);

View File

@ -1,5 +1,6 @@
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Interfaces.Events;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IW4MAdmin.Application.EventParsers
@ -10,7 +11,9 @@ namespace IW4MAdmin.Application.EventParsers
/// </summary>
sealed internal class DynamicEventParser : BaseEventParser
{
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger, ApplicationConfiguration appConfig) : base(parserRegexFactory, logger, appConfig)
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger,
ApplicationConfiguration appConfig, IGameScriptEventFactory gameScriptEventFactory) : base(
parserRegexFactory, logger, appConfig, gameScriptEventFactory)
{
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using SharedLibraryCore.Interfaces.Events;
namespace IW4MAdmin.Application.Factories;
public class GameScriptEventFactory : IGameScriptEventFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly Dictionary<string, Type> _gameScriptEventMap;
public GameScriptEventFactory(IServiceProvider serviceProvider, IEnumerable<IGameScriptEvent> gameScriptEventTypes)
{
_serviceProvider = serviceProvider;
_gameScriptEventMap = gameScriptEventTypes
.ToLookup(kvp => kvp.EventName ?? kvp.GetType().Name.Replace("ScriptEvent", ""))
.ToDictionary(kvp => kvp.Key, kvp => kvp.First().GetType());
}
public IGameScriptEvent Create(string eventType, string logData)
{
if (string.IsNullOrEmpty(eventType) || !_gameScriptEventMap.TryGetValue(eventType, out var matchedType))
{
return null;
}
var newEvent = _serviceProvider.GetRequiredService(matchedType) as IGameScriptEvent;
if (newEvent is not null)
{
newEvent.ScriptData = logData;
}
return newEvent;
}
}

View File

@ -39,6 +39,7 @@ using IW4MAdmin.Plugins.Stats.Client.Abstractions;
using IW4MAdmin.Plugins.Stats.Client;
using Microsoft.Extensions.Hosting;
using Refit;
using SharedLibraryCore.Interfaces.Events;
using Stats.Client.Abstractions;
using Stats.Client;
using Stats.Config;
@ -527,6 +528,7 @@ namespace IW4MAdmin.Application
.AddSingleton(new ConfigurationWatcher())
.AddSingleton(typeof(IConfigurationHandlerV2<>), typeof(BaseConfigurationHandlerV2<>))
.AddSingleton<IScriptPluginFactory, ScriptPluginFactory>()
.AddSingleton<IGameScriptEventFactory, GameScriptEventFactory>()
.AddSingleton(translationLookup)
.AddDatabaseContextOptions(appConfig);

View File

@ -1,12 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
// ReSharper disable CompareOfFloatsByEqualityOperator
#pragma warning disable CS0659
namespace Data.Models
{
public class Vector3
public class Vector3 : IParsable<Vector3>
{
[Key] public int Vector3Id { get; set; }
public float X { get; protected set; }
@ -111,5 +112,30 @@ namespace Data.Models
public double Magnitude() => Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
public double AngleBetween(Vector3 a) => Math.Acos(this.DotProduct(a) / (a.Magnitude() * this.Magnitude()));
public static Vector3 Parse(string s, IFormatProvider provider)
{
return Parse(s);
}
public static bool TryParse(string s, IFormatProvider provider, out Vector3 result)
{
result = new Vector3();
try
{
var parsed = Parse(s);
result.X = parsed.X;
result.Y = parsed.Y;
result.Z = parsed.Z;
return true;
}
catch
{
// ignored
}
return false;
}
}
}

View File

@ -3,7 +3,6 @@ using SharedLibraryCore;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces;
using System.Linq;
using System.Threading.Tasks;
using IW4MAdmin.Plugins.LiveRadar.Configuration;
using Microsoft.AspNetCore.Http;
@ -82,7 +81,7 @@ namespace IW4MAdmin.Plugins.LiveRadar.Web.Controllers
}
var radarInfo = server.GetClientsAsList()
.Select(client => client.GetAdditionalProperty<RadarEvent>("LiveRadar")).ToList();
.Select(client => client.GetAdditionalProperty<RadarDto>("LiveRadar")).ToList();
return Json(radarInfo);
}

View File

@ -1,38 +0,0 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using SharedLibraryCore.Events.Game;
using EventGeneratorCallback = System.ValueTuple<string, string,
System.Func<string, SharedLibraryCore.Interfaces.IEventParserConfiguration,
SharedLibraryCore.GameEvent,
SharedLibraryCore.GameEvent>>;
namespace IW4MAdmin.Plugins.LiveRadar.Events;
public class Script : IRegisterEvent
{
private const string EventLiveRadar = "LiveRadar";
private EventGeneratorCallback LiveRadar()
{
return (EventLiveRadar, EventLiveRadar, (eventLine, _, _) =>
{
var radarEvent = new LiveRadarEvent
{
Type = GameEvent.EventType.Other,
Subtype = EventLiveRadar,
Origin = new EFClient { NetworkId = 0 },
ScriptData = eventLine
};
return radarEvent;
}
);
}
public IEnumerable<EventGeneratorCallback> Events => new[] { LiveRadar() };
}
public class LiveRadarEvent : GameScriptEvent
{
}

View File

@ -0,0 +1,19 @@
using Data.Models;
using SharedLibraryCore.Events.Game;
namespace IW4MAdmin.Plugins.LiveRadar.Events;
public class LiveRadarScriptEvent : GameScriptEvent
{
public string Name { get; set; }
public Vector3 Location { get; set; }
public Vector3 ViewAngles { get; set; }
public string Team { get; set; }
public int Kills { get; set; }
public int Deaths { get; set; }
public int Score { get; set; }
public string Weapon { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
public int PlayTime { get; set; }
}

View File

@ -15,8 +15,11 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.9" PrivateAssets="All" />
<ItemGroup><!--<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.9" PrivateAssets="All" />-->
<ProjectReference Include="..\..\Data\Data.csproj" />
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">

View File

@ -36,6 +36,9 @@ public class Plugin : IPluginV2
public static void RegisterDependencies(IServiceCollection serviceCollection)
{
serviceCollection.AddConfiguration<LiveRadarConfiguration>();
serviceCollection.AddSingleton<IGameScriptEvent, LiveRadarScriptEvent>(); // for identification
serviceCollection.AddTransient<LiveRadarScriptEvent>(); // for factory
}
public Plugin(ILogger<Plugin> logger, ApplicationConfiguration appConfig)
@ -51,7 +54,7 @@ public class Plugin : IPluginV2
private Task OnScriptEvent(GameScriptEvent scriptEvent, CancellationToken token)
{
if (scriptEvent is not LiveRadarEvent radarEvent)
if (scriptEvent is not LiveRadarScriptEvent radarEvent)
{
return Task.CompletedTask;
}
@ -83,14 +86,15 @@ public class Plugin : IPluginV2
: (originalBotGuid ?? "0").ConvertGuidToLong(NumberStyles.HexNumber);
}
var radarUpdate = RadarEvent.Parse(scriptEvent.ScriptData, generatedBotGuid);
var radarDto = RadarDto.FromScriptEvent(radarEvent, generatedBotGuid);
var client =
radarEvent.Owner.ConnectedClients.FirstOrDefault(client => client.NetworkId == radarUpdate.Guid);
radarEvent.Owner.ConnectedClients.FirstOrDefault(client => client.NetworkId == radarDto.Guid);
if (client != null)
{
radarUpdate.Name = client.Name.StripColors();
client.SetAdditionalProperty("LiveRadar", radarUpdate);
radarDto.Name = client.Name.StripColors();
client.SetAdditionalProperty("LiveRadar", radarDto);
}
}

View File

@ -1,13 +1,13 @@
using Data.Models;
using SharedLibraryCore;
using System;
using System.Linq;
using IW4MAdmin.Plugins.LiveRadar.Events;
// ReSharper disable CompareOfFloatsByEqualityOperator
#pragma warning disable CS0659
namespace IW4MAdmin.Plugins.LiveRadar;
public class RadarEvent
public class RadarDto
{
public string Name { get; set; }
public long Guid { get; set; }
@ -26,7 +26,7 @@ public class RadarEvent
public override bool Equals(object obj)
{
if (obj is RadarEvent re)
if (obj is RadarDto re)
{
return re.ViewAngles.X == ViewAngles.X &&
re.ViewAngles.Y == ViewAngles.Y &&
@ -39,23 +39,21 @@ public class RadarEvent
return false;
}
public static RadarEvent Parse(string input, long generatedBotGuid)
public static RadarDto FromScriptEvent(LiveRadarScriptEvent scriptEvent, long generatedBotGuid)
{
var items = input.Split(';').Skip(1).ToList();
var parsedEvent = new RadarEvent()
var parsedEvent = new RadarDto
{
Guid = generatedBotGuid,
Location = Vector3.Parse(items[1]),
ViewAngles = Vector3.Parse(items[2]).FixIW4Angles(),
Team = items[3],
Kills = int.Parse(items[4]),
Deaths = int.Parse(items[5]),
Score = int.Parse(items[6]),
Weapon = items[7],
Health = int.Parse(items[8]),
IsAlive = items[9] == "1",
PlayTime = Convert.ToInt32(items[10])
Location = scriptEvent.Location,
ViewAngles = scriptEvent.ViewAngles.FixIW4Angles(),
Team = scriptEvent.Team,
Kills = scriptEvent.Kills,
Deaths = scriptEvent.Deaths,
Score = scriptEvent.Score,
Weapon =scriptEvent.Weapon,
Health = scriptEvent.Health,
IsAlive = scriptEvent.IsAlive,
PlayTime = scriptEvent.PlayTime
};
return parsedEvent;

View File

@ -1,6 +1,77 @@
namespace SharedLibraryCore.Events.Game;
using System;
using System.Reflection;
using SharedLibraryCore.Interfaces.Events;
public class GameScriptEvent : GameEventV2
namespace SharedLibraryCore.Events.Game;
public class GameScriptEvent : GameEventV2, IGameScriptEvent
{
public string ScriptData { get; init; }
public string ScriptData { get; set; }
public string EventName { get; } = null;
public virtual void ParseArguments()
{
var arguments = ScriptData.Split(';', StringSplitOptions.RemoveEmptyEntries);
var propIndex = 0;
foreach (var argument in arguments)
{
var parts = argument.Split(['='], 2);
PropertyInfo propertyInfo = null;
string rawValue;
if (parts.Length == 2) // handle as key/value pairs
{
var propertyName = parts[0].Trim();
rawValue = parts[1].Trim();
propertyInfo = GetType().GetProperty(propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly);
}
else
{
rawValue = argument;
try
{
propertyInfo =
GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase |
BindingFlags.DeclaredOnly)[
propIndex];
}
catch
{
// ignored
}
}
if (propertyInfo is null)
{
continue;
}
try
{
var method = propertyInfo.PropertyType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public,
[typeof(string)]);
var convertedValue = method is not null
? method!.Invoke(null, [rawValue])!
: Convert.ChangeType(rawValue, propertyInfo.PropertyType);
propertyInfo.SetValue(this, convertedValue);
}
catch (TargetInvocationException ex) when (ex.InnerException is FormatException &&
propertyInfo.PropertyType == typeof(bool))
{
propertyInfo.SetValue(this, rawValue != "0");
}
catch
{
// ignored
}
propIndex++;
}
}
}

View File

@ -0,0 +1,8 @@
namespace SharedLibraryCore.Interfaces.Events;
public interface IGameScriptEvent
{
string ScriptData { get; set; }
string EventName { get; }
void ParseArguments();
}

View File

@ -0,0 +1,6 @@
namespace SharedLibraryCore.Interfaces.Events;
public interface IGameScriptEventFactory
{
IGameScriptEvent Create(string eventType, string logData);
}