2021-12-26 18:14:06 -05:00
|
|
|
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
2018-11-27 19:31:48 -05:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2018-04-08 17:50:58 -04:00
|
|
|
|
using SharedLibraryCore;
|
2020-08-17 22:21:11 -04:00
|
|
|
|
using SharedLibraryCore.Dtos.Meta.Responses;
|
2018-04-08 17:50:58 -04:00
|
|
|
|
using SharedLibraryCore.Helpers;
|
|
|
|
|
using SharedLibraryCore.Interfaces;
|
2020-08-17 22:21:11 -04:00
|
|
|
|
using SharedLibraryCore.QueryHelper;
|
2020-08-20 11:38:11 -04:00
|
|
|
|
using Stats.Dtos;
|
2018-11-27 19:31:48 -05:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2022-03-23 14:54:42 -04:00
|
|
|
|
using System.Threading;
|
2018-11-27 19:31:48 -05:00
|
|
|
|
using System.Threading.Tasks;
|
2021-03-22 12:09:25 -04:00
|
|
|
|
using Data.Abstractions;
|
|
|
|
|
using Data.Models.Client.Stats;
|
|
|
|
|
using Data.Models.Server;
|
2020-11-11 18:31:26 -05:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2021-03-22 12:09:25 -04:00
|
|
|
|
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
|
|
|
|
using Stats.Client.Abstractions;
|
2021-11-23 18:26:33 -05:00
|
|
|
|
using Stats.Config;
|
2021-03-22 12:09:25 -04:00
|
|
|
|
using EFClient = SharedLibraryCore.Database.Models.EFClient;
|
2017-11-25 20:29:58 -05:00
|
|
|
|
|
2018-04-08 17:50:58 -04:00
|
|
|
|
namespace IW4MAdmin.Plugins.Stats
|
2015-08-20 01:06:44 -04:00
|
|
|
|
{
|
2018-05-28 21:30:31 -04:00
|
|
|
|
public class Plugin : IPlugin
|
2015-08-20 01:06:44 -04:00
|
|
|
|
{
|
2018-02-07 00:19:06 -05:00
|
|
|
|
public string Name => "Simple Stats";
|
2017-06-01 13:42:28 -04:00
|
|
|
|
|
2020-01-17 18:31:53 -05:00
|
|
|
|
public float Version => (float)Utilities.GetVersionAsDouble();
|
2017-05-26 18:49:27 -04:00
|
|
|
|
|
2017-11-15 16:04:13 -05:00
|
|
|
|
public string Author => "RaidMax";
|
2017-05-26 18:49:27 -04:00
|
|
|
|
|
2018-03-06 02:22:19 -05:00
|
|
|
|
public static StatManager Manager { get; private set; }
|
2019-02-26 22:25:27 -05:00
|
|
|
|
public static IManager ServerManager;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
public static IConfigurationHandler<StatsConfiguration> Config { get; private set; }
|
2020-11-11 18:31:26 -05:00
|
|
|
|
|
2020-04-25 20:01:26 -04:00
|
|
|
|
private readonly IDatabaseContextFactory _databaseContextFactory;
|
2020-05-05 19:49:30 -04:00
|
|
|
|
private readonly ITranslationLookup _translationLookup;
|
2022-03-23 14:54:42 -04:00
|
|
|
|
private readonly IMetaServiceV2 _metaService;
|
2020-08-20 11:38:11 -04:00
|
|
|
|
private readonly IResourceQueryHelper<ChatSearchQuery, MessageResponse> _chatQueryHelper;
|
2020-11-11 18:31:26 -05:00
|
|
|
|
private readonly ILogger<StatManager> _managerLogger;
|
|
|
|
|
private readonly ILogger<Plugin> _logger;
|
2021-03-22 12:09:25 -04:00
|
|
|
|
private readonly List<IClientStatisticCalculator> _statCalculators;
|
|
|
|
|
private readonly IServerDistributionCalculator _serverDistributionCalculator;
|
2022-06-09 10:56:41 -04:00
|
|
|
|
private readonly IServerDataViewer _serverDataViewer;
|
2017-05-26 18:49:27 -04:00
|
|
|
|
|
2020-11-11 18:31:26 -05:00
|
|
|
|
public Plugin(ILogger<Plugin> logger, IConfigurationHandlerFactory configurationHandlerFactory, IDatabaseContextFactory databaseContextFactory,
|
2022-03-23 14:54:42 -04:00
|
|
|
|
ITranslationLookup translationLookup, IMetaServiceV2 metaService, IResourceQueryHelper<ChatSearchQuery, MessageResponse> chatQueryHelper, ILogger<StatManager> managerLogger,
|
2022-06-09 10:56:41 -04:00
|
|
|
|
IEnumerable<IClientStatisticCalculator> statCalculators, IServerDistributionCalculator serverDistributionCalculator, IServerDataViewer serverDataViewer)
|
2020-02-11 17:44:06 -05:00
|
|
|
|
{
|
|
|
|
|
Config = configurationHandlerFactory.GetConfigurationHandler<StatsConfiguration>("StatsPluginSettings");
|
2020-04-25 20:01:26 -04:00
|
|
|
|
_databaseContextFactory = databaseContextFactory;
|
2020-05-05 19:49:30 -04:00
|
|
|
|
_translationLookup = translationLookup;
|
2020-08-17 22:21:11 -04:00
|
|
|
|
_metaService = metaService;
|
2020-08-20 11:38:11 -04:00
|
|
|
|
_chatQueryHelper = chatQueryHelper;
|
2020-11-11 18:31:26 -05:00
|
|
|
|
_managerLogger = managerLogger;
|
|
|
|
|
_logger = logger;
|
2021-03-22 12:09:25 -04:00
|
|
|
|
_statCalculators = statCalculators.ToList();
|
|
|
|
|
_serverDistributionCalculator = serverDistributionCalculator;
|
2022-06-09 10:56:41 -04:00
|
|
|
|
_serverDataViewer = serverDataViewer;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
public async Task OnEventAsync(GameEvent gameEvent, Server server)
|
2017-05-26 18:49:27 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
switch (gameEvent.Type)
|
2015-08-23 17:58:48 -04:00
|
|
|
|
{
|
2018-04-13 02:32:30 -04:00
|
|
|
|
case GameEvent.EventType.Start:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
Manager.AddServer(server);
|
2015-08-23 17:58:48 -04:00
|
|
|
|
break;
|
2018-04-13 02:32:30 -04:00
|
|
|
|
case GameEvent.EventType.Disconnect:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await Manager.RemovePlayer(gameEvent.Origin);
|
2018-02-07 00:19:06 -05:00
|
|
|
|
break;
|
2018-04-13 02:32:30 -04:00
|
|
|
|
case GameEvent.EventType.Say:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (!string.IsNullOrEmpty(gameEvent.Data) &&
|
|
|
|
|
gameEvent.Origin.ClientId > 1)
|
2018-11-27 19:31:48 -05:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await Manager.AddMessageAsync(gameEvent.Origin.ClientId, StatManager.GetIdForServer(server), true, gameEvent.Data);
|
2018-11-27 19:31:48 -05:00
|
|
|
|
}
|
2018-02-07 00:19:06 -05:00
|
|
|
|
break;
|
2018-04-13 02:32:30 -04:00
|
|
|
|
case GameEvent.EventType.MapChange:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
Manager.SetTeamBased(StatManager.GetIdForServer(server), server.Gametype != "dm");
|
|
|
|
|
Manager.ResetKillstreaks(server);
|
|
|
|
|
await Manager.Sync(server);
|
2018-02-07 00:19:06 -05:00
|
|
|
|
break;
|
2018-04-13 02:32:30 -04:00
|
|
|
|
case GameEvent.EventType.MapEnd:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
Manager.ResetKillstreaks(server);
|
|
|
|
|
await Manager.Sync(server);
|
2018-02-07 00:19:06 -05:00
|
|
|
|
break;
|
2020-11-18 10:08:24 -05:00
|
|
|
|
case GameEvent.EventType.Command:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var shouldPersist = !string.IsNullOrEmpty(gameEvent.Data) &&
|
|
|
|
|
gameEvent.Extra?.GetType().Name == "SayCommand";
|
2020-11-18 10:08:24 -05:00
|
|
|
|
if (shouldPersist)
|
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await Manager.AddMessageAsync(gameEvent.Origin.ClientId, StatManager.GetIdForServer(server), false, gameEvent.Data);
|
2020-11-18 10:08:24 -05:00
|
|
|
|
}
|
2018-02-07 00:19:06 -05:00
|
|
|
|
break;
|
2018-05-10 01:34:29 -04:00
|
|
|
|
case GameEvent.EventType.ScriptKill:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var killInfo = (gameEvent.Data != null) ? gameEvent.Data.Split(';') : Array.Empty<string>();
|
|
|
|
|
if ((server.CustomCallback || ShouldOverrideAnticheatSetting(server)) && killInfo.Length >= 18 && !ShouldIgnoreEvent(gameEvent.Origin, gameEvent.Target))
|
2018-08-30 21:53:00 -04:00
|
|
|
|
{
|
2019-04-05 22:15:17 -04:00
|
|
|
|
// this treats "world" damage as self damage
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (IsWorldDamage(gameEvent.Origin))
|
2019-04-05 22:15:17 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
gameEvent.Origin = gameEvent.Target;
|
2019-04-05 22:15:17 -04:00
|
|
|
|
}
|
2020-11-18 17:28:14 -05:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await EnsureClientsAdded(gameEvent.Origin, gameEvent.Target);
|
|
|
|
|
await Manager.AddScriptHit(false, gameEvent.Time, gameEvent.Origin, gameEvent.Target, StatManager.GetIdForServer(server), server.CurrentMap.Name, killInfo[7], killInfo[8],
|
2020-01-06 19:43:00 -05:00
|
|
|
|
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15], killInfo[16], killInfo[17]);
|
2018-08-30 21:53:00 -04:00
|
|
|
|
}
|
2020-01-17 18:31:53 -05:00
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-11-11 18:31:26 -05:00
|
|
|
|
_logger.LogDebug("Skipping script kill as it is ignored or data in customcallbacks is outdated/missing");
|
2020-01-17 18:31:53 -05:00
|
|
|
|
}
|
2018-05-10 01:34:29 -04:00
|
|
|
|
break;
|
|
|
|
|
case GameEvent.EventType.Kill:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (!ShouldIgnoreEvent(gameEvent.Origin, gameEvent.Target))
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2019-04-02 21:20:37 -04:00
|
|
|
|
// this treats "world" damage as self damage
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (IsWorldDamage(gameEvent.Origin))
|
2019-04-02 21:20:37 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
gameEvent.Origin = gameEvent.Target;
|
2019-04-02 21:20:37 -04:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await EnsureClientsAdded(gameEvent.Origin, gameEvent.Target);
|
|
|
|
|
await Manager.AddStandardKill(gameEvent.Origin, gameEvent.Target);
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-02-07 00:19:06 -05:00
|
|
|
|
break;
|
2018-05-10 01:34:29 -04:00
|
|
|
|
case GameEvent.EventType.Damage:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (!ShouldIgnoreEvent(gameEvent.Origin, gameEvent.Target))
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2019-04-02 21:20:37 -04:00
|
|
|
|
// this treats "world" damage as self damage
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (IsWorldDamage(gameEvent.Origin))
|
2019-04-02 21:20:37 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
gameEvent.Origin = gameEvent.Target;
|
2019-04-02 21:20:37 -04:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
Manager.AddDamageEvent(gameEvent.Data, gameEvent.Origin.ClientId, gameEvent.Target.ClientId, StatManager.GetIdForServer(server));
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-05-10 01:34:29 -04:00
|
|
|
|
break;
|
2018-05-08 00:58:46 -04:00
|
|
|
|
case GameEvent.EventType.ScriptDamage:
|
2022-03-23 14:54:42 -04:00
|
|
|
|
killInfo = (gameEvent.Data != null) ? gameEvent.Data.Split(';') : new string[0];
|
|
|
|
|
if ((server.CustomCallback || ShouldOverrideAnticheatSetting(server)) && killInfo.Length >= 18 && !ShouldIgnoreEvent(gameEvent.Origin, gameEvent.Target))
|
2018-08-30 21:53:00 -04:00
|
|
|
|
{
|
2019-04-05 22:15:17 -04:00
|
|
|
|
// this treats "world" damage as self damage
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (IsWorldDamage(gameEvent.Origin))
|
2019-04-05 22:15:17 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
gameEvent.Origin = gameEvent.Target;
|
2019-04-05 22:15:17 -04:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await EnsureClientsAdded(gameEvent.Origin, gameEvent.Target);
|
|
|
|
|
await Manager.AddScriptHit(true, gameEvent.Time, gameEvent.Origin, gameEvent.Target, StatManager.GetIdForServer(server), server.CurrentMap.Name, killInfo[7], killInfo[8],
|
2020-01-06 19:43:00 -05:00
|
|
|
|
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15], killInfo[16], killInfo[17]);
|
2019-09-09 18:40:04 -04:00
|
|
|
|
}
|
2020-01-17 18:31:53 -05:00
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-11-11 18:31:26 -05:00
|
|
|
|
_logger.LogDebug("Skipping script damage as it is ignored or data in customcallbacks is outdated/missing");
|
2020-01-17 18:31:53 -05:00
|
|
|
|
}
|
2018-05-03 01:25:49 -04:00
|
|
|
|
break;
|
2015-08-20 17:54:38 -04:00
|
|
|
|
}
|
2021-03-22 12:09:25 -04:00
|
|
|
|
|
|
|
|
|
if (!Config.Configuration().EnableAdvancedMetrics)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var calculator in _statCalculators)
|
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await calculator.CalculateForEvent(gameEvent);
|
2021-03-22 12:09:25 -04:00
|
|
|
|
}
|
2015-08-20 17:54:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-18 22:25:11 -04:00
|
|
|
|
public async Task OnLoadAsync(IManager manager)
|
2015-08-20 17:54:38 -04:00
|
|
|
|
{
|
2022-01-28 10:35:01 -05:00
|
|
|
|
await Config.BuildAsync();
|
2018-03-18 22:25:11 -04:00
|
|
|
|
// load custom configuration
|
2018-03-22 14:50:09 -04:00
|
|
|
|
if (Config.Configuration() == null)
|
2018-03-18 22:25:11 -04:00
|
|
|
|
{
|
|
|
|
|
Config.Set((StatsConfiguration)new StatsConfiguration().Generate());
|
|
|
|
|
}
|
2020-09-30 18:15:47 -04:00
|
|
|
|
Config.Configuration().ApplyMigration();
|
|
|
|
|
await Config.Save();
|
2018-03-18 22:25:11 -04:00
|
|
|
|
|
2018-08-03 18:10:20 -04:00
|
|
|
|
// register the topstats page
|
|
|
|
|
// todo:generate the URL/Location instead of hardcoding
|
|
|
|
|
manager.GetPageList()
|
|
|
|
|
.Pages.Add(
|
|
|
|
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_TEXT"],
|
2022-04-19 23:34:35 -04:00
|
|
|
|
"/Stats/TopPlayers");
|
2018-08-03 18:10:20 -04:00
|
|
|
|
|
2018-02-14 14:01:26 -05:00
|
|
|
|
// meta data info
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<IEnumerable<InformationResponse>> GetStats(ClientPaginationRequest request, CancellationToken token = default)
|
2018-02-14 14:01:26 -05:00
|
|
|
|
{
|
2020-11-29 17:01:52 -05:00
|
|
|
|
await using var ctx = _databaseContextFactory.CreateContext(enableTracking: false);
|
2022-03-23 14:54:42 -04:00
|
|
|
|
IList<EFClientStatistics> clientStats = await ctx.Set<EFClientStatistics>().Where(c => c.ClientId == request.ClientId).ToListAsync(token);
|
2018-02-14 14:01:26 -05:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var kills = clientStats.Sum(c => c.Kills);
|
|
|
|
|
var deaths = clientStats.Sum(c => c.Deaths);
|
|
|
|
|
var kdr = Math.Round(kills / (double)deaths, 2);
|
|
|
|
|
var validPerformanceValues = clientStats.Where(c => c.Performance > 0).ToList();
|
|
|
|
|
var performancePlayTime = validPerformanceValues.Sum(s => s.TimePlayed);
|
|
|
|
|
var performance = Math.Round(validPerformanceValues.Sum(c => c.Performance * c.TimePlayed / performancePlayTime), 2);
|
|
|
|
|
var spm = Math.Round(clientStats.Sum(c => c.SPM) / clientStats.Count(c => c.SPM > 0), 1);
|
2022-06-09 10:56:41 -04:00
|
|
|
|
var overallRanking = await Manager.GetClientOverallRanking(request.ClientId);
|
2018-02-14 14:01:26 -05:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
return new List<InformationResponse>
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
new InformationResponse
|
2018-09-04 22:07:34 -04:00
|
|
|
|
{
|
|
|
|
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
2022-06-09 10:56:41 -04:00
|
|
|
|
Value = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING_FORMAT"].FormatExt((overallRanking == 0 ? "--" :
|
|
|
|
|
overallRanking.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))),
|
|
|
|
|
(await _serverDataViewer.RankedClientsCountAsync(token: token)).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))
|
|
|
|
|
),
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Column = 0,
|
|
|
|
|
Order = 0,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information
|
2018-09-04 22:07:34 -04:00
|
|
|
|
},
|
2022-03-23 14:54:42 -04:00
|
|
|
|
new InformationResponse
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2018-05-08 00:58:46 -04:00
|
|
|
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"],
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Value = kills.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
|
|
|
|
Column = 0,
|
|
|
|
|
Order = 1,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2022-03-23 14:54:42 -04:00
|
|
|
|
new InformationResponse
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2018-05-08 00:58:46 -04:00
|
|
|
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"],
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Value = deaths.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
|
|
|
|
Column = 0,
|
|
|
|
|
Order = 2,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2022-03-23 14:54:42 -04:00
|
|
|
|
new InformationResponse
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2018-05-08 00:58:46 -04:00
|
|
|
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"],
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Value = kdr.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
|
|
|
|
Column = 0,
|
|
|
|
|
Order = 3,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2022-03-23 14:54:42 -04:00
|
|
|
|
new InformationResponse
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PERFORMANCE"],
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Value = performance.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
|
|
|
|
Column = 0,
|
|
|
|
|
Order = 4,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2022-03-23 14:54:42 -04:00
|
|
|
|
new InformationResponse
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2018-05-08 00:58:46 -04:00
|
|
|
|
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_META_SPM"],
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Value = spm.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
|
|
|
|
Column = 0,
|
|
|
|
|
Order = 5,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information
|
2018-04-05 00:38:45 -04:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<IEnumerable<InformationResponse>> GetAnticheatInfo(ClientPaginationRequest request, CancellationToken token = default)
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
await using var context = _databaseContextFactory.CreateContext(enableTracking: false);
|
|
|
|
|
IList<EFClientStatistics> clientStats = await context.Set<EFClientStatistics>()
|
2020-11-29 17:01:52 -05:00
|
|
|
|
.Include(c => c.HitLocations)
|
|
|
|
|
.Where(c => c.ClientId == request.ClientId)
|
2022-03-23 14:54:42 -04:00
|
|
|
|
.ToListAsync(token);
|
2018-04-05 00:38:45 -04:00
|
|
|
|
|
2018-03-22 14:50:09 -04:00
|
|
|
|
double headRatio = 0;
|
2018-03-06 02:22:19 -05:00
|
|
|
|
double chestRatio = 0;
|
|
|
|
|
double abdomenRatio = 0;
|
|
|
|
|
double chestAbdomenRatio = 0;
|
2018-03-27 20:27:01 -04:00
|
|
|
|
double hitOffsetAverage = 0;
|
2019-09-09 18:40:04 -04:00
|
|
|
|
double averageSnapValue = 0;
|
2022-03-24 09:40:42 -04:00
|
|
|
|
var maxStrain = !clientStats.Any(c => c.MaxStrain > 0) ? 0 : clientStats.Max(cs => cs.MaxStrain);
|
2018-03-06 02:22:19 -05:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
if (clientStats.Any(cs => cs.HitLocations.Count > 0))
|
2018-03-06 02:22:19 -05:00
|
|
|
|
{
|
2019-04-21 17:28:13 -04:00
|
|
|
|
chestRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
2021-03-22 12:09:25 -04:00
|
|
|
|
c.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_upper).HitCount) /
|
2018-03-06 02:22:19 -05:00
|
|
|
|
(double)clientStats.Where(c => c.HitLocations.Count > 0)
|
2021-03-22 12:09:25 -04:00
|
|
|
|
.Sum(c => c.HitLocations.Where(hl => hl.Location != (int)IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0);
|
2018-03-06 02:22:19 -05:00
|
|
|
|
|
2019-04-21 17:28:13 -04:00
|
|
|
|
abdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
2021-03-22 12:09:25 -04:00
|
|
|
|
c.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_lower).HitCount) /
|
|
|
|
|
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != (int)IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0);
|
2018-03-06 02:22:19 -05:00
|
|
|
|
|
2021-03-22 12:09:25 -04:00
|
|
|
|
chestAbdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_upper).HitCount) /
|
|
|
|
|
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.torso_lower).HitCount)) * 100.0, 0);
|
2018-03-22 14:50:09 -04:00
|
|
|
|
|
2021-03-22 12:09:25 -04:00
|
|
|
|
headRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == (int)IW4Info.HitLocation.head).HitCount) /
|
2018-03-22 14:50:09 -04:00
|
|
|
|
(double)clientStats.Where(c => c.HitLocations.Count > 0)
|
2021-03-22 12:09:25 -04:00
|
|
|
|
.Sum(c => c.HitLocations.Where(hl => hl.Location != (int)IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0);
|
2018-03-27 20:27:01 -04:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var validOffsets = clientStats.Where(c => c.HitLocations.Count(hl => hl.HitCount > 0) > 0).SelectMany(hl => hl.HitLocations).ToList();
|
2018-05-05 16:36:26 -04:00
|
|
|
|
hitOffsetAverage = validOffsets.Sum(o => o.HitCount * o.HitOffsetAverage) / (double)validOffsets.Sum(o => o.HitCount);
|
2019-09-09 18:40:04 -04:00
|
|
|
|
averageSnapValue = clientStats.Any(_stats => _stats.AverageSnapValue > 0) ? clientStats.Where(_stats => _stats.AverageSnapValue > 0).Average(_stat => _stat.AverageSnapValue) : 0;
|
2018-03-06 02:22:19 -05:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
return new List<InformationResponse>
|
2018-02-14 14:01:26 -05:00
|
|
|
|
{
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 1",
|
2019-04-21 17:28:13 -04:00
|
|
|
|
Value = chestRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 100,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM1"],
|
|
|
|
|
IsSensitive = true
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 2",
|
2019-04-21 17:28:13 -04:00
|
|
|
|
Value = abdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 101,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM2"],
|
|
|
|
|
IsSensitive = true
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 3",
|
2019-04-21 17:28:13 -04:00
|
|
|
|
Value = chestAbdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 102,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM3"],
|
|
|
|
|
IsSensitive = true
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 4",
|
2019-04-21 17:28:13 -04:00
|
|
|
|
Value = headRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 103,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM4"],
|
|
|
|
|
IsSensitive = true
|
2018-04-05 00:38:45 -04:00
|
|
|
|
},
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2018-04-05 00:38:45 -04:00
|
|
|
|
{
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 5",
|
2018-10-05 23:10:39 -04:00
|
|
|
|
// todo: make sure this is wrapped somewhere else
|
|
|
|
|
Value = $"{Math.Round(((float)hitOffsetAverage), 4).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))}°",
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 104,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM5"],
|
|
|
|
|
IsSensitive = true
|
2018-05-03 01:25:49 -04:00
|
|
|
|
},
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2018-05-03 01:25:49 -04:00
|
|
|
|
{
|
2019-03-30 18:21:01 -04:00
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 6",
|
|
|
|
|
Value = Math.Round(maxStrain, 3).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 105,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM6"],
|
|
|
|
|
IsSensitive = true
|
2018-05-03 01:25:49 -04:00
|
|
|
|
},
|
2020-08-17 22:21:11 -04:00
|
|
|
|
new InformationResponse()
|
2019-06-15 18:37:43 -04:00
|
|
|
|
{
|
|
|
|
|
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 7",
|
2019-09-09 18:40:04 -04:00
|
|
|
|
Value = Math.Round(averageSnapValue, 3).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
2020-08-17 22:21:11 -04:00
|
|
|
|
Type = MetaType.Information,
|
2022-04-19 23:34:35 -04:00
|
|
|
|
Order = 106,
|
2020-08-17 22:21:11 -04:00
|
|
|
|
ToolTipText = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM7"],
|
|
|
|
|
IsSensitive = true
|
2019-09-09 18:40:04 -04:00
|
|
|
|
}
|
2018-02-14 14:01:26 -05:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<IEnumerable<MessageResponse>> GetMessages(ClientPaginationRequest request, CancellationToken token = default)
|
2018-02-14 14:01:26 -05:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var query = new ChatSearchQuery
|
2018-02-14 14:01:26 -05:00
|
|
|
|
{
|
2020-08-20 11:38:11 -04:00
|
|
|
|
ClientId = request.ClientId,
|
|
|
|
|
Before = request.Before,
|
|
|
|
|
SentBefore = request.Before ?? DateTime.UtcNow,
|
|
|
|
|
Count = request.Count,
|
|
|
|
|
IsProfileMeta = true
|
|
|
|
|
};
|
2018-10-07 22:34:30 -04:00
|
|
|
|
|
2020-08-20 11:38:11 -04:00
|
|
|
|
return (await _chatQueryHelper.QueryResource(query)).Results;
|
2018-02-14 14:01:26 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 18:15:47 -04:00
|
|
|
|
if (Config.Configuration().AnticheatConfiguration.Enable)
|
2018-03-22 14:50:09 -04:00
|
|
|
|
{
|
2022-03-23 14:54:42 -04:00
|
|
|
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information, GetAnticheatInfo);
|
2018-03-22 14:50:09 -04:00
|
|
|
|
}
|
2018-04-05 00:38:45 -04:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, InformationResponse>(MetaType.Information, GetStats);
|
|
|
|
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, MessageResponse>(MetaType.ChatMessage, GetMessages);
|
2018-02-14 14:01:26 -05:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<string> TotalKills(Server server)
|
2018-02-09 02:21:25 -05:00
|
|
|
|
{
|
2020-11-27 22:52:52 -05:00
|
|
|
|
await using var context = _databaseContextFactory.CreateContext(false);
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var kills = await context.Set<EFServerStatistics>().Where(s => s.Active).SumAsync(s => s.TotalKills);
|
2020-11-27 22:52:52 -05:00
|
|
|
|
return kills.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName));
|
2018-02-09 02:21:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<string> TotalPlayTime(Server server)
|
2018-02-09 02:21:25 -05:00
|
|
|
|
{
|
2020-11-27 22:52:52 -05:00
|
|
|
|
await using var context = _databaseContextFactory.CreateContext(false);
|
2022-03-23 14:54:42 -04:00
|
|
|
|
var playTime = await context.Set<EFServerStatistics>().Where(s => s.Active).SumAsync(s => s.TotalPlayTime);
|
2020-11-27 22:52:52 -05:00
|
|
|
|
return (playTime / 3600.0).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName));
|
2018-02-09 02:21:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<string> TopStats(Server s)
|
2018-05-05 16:36:26 -04:00
|
|
|
|
{
|
2020-01-31 21:15:07 -05:00
|
|
|
|
// todo: this needs to needs to be updated when we DI the lookup
|
2021-03-22 12:09:25 -04:00
|
|
|
|
return string.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s, Utilities.CurrentLocalization.LocalizationIndex));
|
2018-05-05 16:36:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<string> MostPlayed(Server s)
|
2018-05-21 17:09:27 -04:00
|
|
|
|
{
|
2020-01-31 21:15:07 -05:00
|
|
|
|
// todo: this needs to needs to be updated when we DI the lookup
|
2020-11-27 22:52:52 -05:00
|
|
|
|
return string.Join(Environment.NewLine, await Commands.MostPlayedCommand.GetMostPlayed(s, Utilities.CurrentLocalization.LocalizationIndex, _databaseContextFactory));
|
2018-05-21 17:09:27 -04:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
async Task<string> MostKills(Server gameServer)
|
2020-05-05 19:49:30 -04:00
|
|
|
|
{
|
|
|
|
|
return string.Join(Environment.NewLine,
|
|
|
|
|
await Commands.MostKillsCommand.GetMostKills(StatManager.GetIdForServer(gameServer), Config.Configuration(), _databaseContextFactory, _translationLookup));
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", TotalKills));
|
|
|
|
|
manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYTIME", TotalPlayTime));
|
|
|
|
|
manager.GetMessageTokens().Add(new MessageToken("TOPSTATS", TopStats));
|
|
|
|
|
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", MostPlayed));
|
|
|
|
|
manager.GetMessageTokens().Add(new MessageToken("MOSTKILLS", MostKills));
|
2018-02-10 01:26:38 -05:00
|
|
|
|
|
2021-03-22 12:09:25 -04:00
|
|
|
|
if (Config.Configuration().EnableAdvancedMetrics)
|
|
|
|
|
{
|
|
|
|
|
foreach (var calculator in _statCalculators)
|
|
|
|
|
{
|
|
|
|
|
await calculator.GatherDependencies();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-10 01:26:38 -05:00
|
|
|
|
ServerManager = manager;
|
2022-01-28 18:28:49 -05:00
|
|
|
|
Manager = new StatManager(_managerLogger, manager, _databaseContextFactory, Config.Configuration(), _serverDistributionCalculator);
|
2021-03-22 12:09:25 -04:00
|
|
|
|
await _serverDistributionCalculator.Initialize();
|
2017-05-31 01:31:56 -04:00
|
|
|
|
}
|
2015-08-20 17:54:38 -04:00
|
|
|
|
|
2022-03-23 14:54:42 -04:00
|
|
|
|
public Task OnTickAsync(Server server)
|
2018-11-27 19:31:48 -05:00
|
|
|
|
{
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2015-08-20 17:54:38 -04:00
|
|
|
|
|
2018-02-10 01:26:38 -05:00
|
|
|
|
public async Task OnUnloadAsync()
|
2015-08-20 17:54:38 -04:00
|
|
|
|
{
|
2018-02-10 01:26:38 -05:00
|
|
|
|
foreach (var sv in ServerManager.GetServers())
|
2018-11-27 19:31:48 -05:00
|
|
|
|
{
|
2018-02-10 01:26:38 -05:00
|
|
|
|
await Manager.Sync(sv);
|
2018-11-27 19:31:48 -05:00
|
|
|
|
}
|
2015-08-20 17:54:38 -04:00
|
|
|
|
}
|
2019-04-25 14:00:54 -04:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates if the event should be ignored
|
|
|
|
|
/// (If the client id or target id is not a real client or the target/origin is a bot and ignore bots is turned on)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="origin"></param>
|
|
|
|
|
/// <param name="target"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private bool ShouldIgnoreEvent(EFClient origin, EFClient target)
|
|
|
|
|
{
|
2020-05-16 21:55:18 -04:00
|
|
|
|
return ((origin?.NetworkId == Utilities.WORLD_ID && target?.NetworkId == Utilities.WORLD_ID));
|
2019-04-25 14:00:54 -04:00
|
|
|
|
}
|
2019-05-29 17:55:35 -04:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates if the damage occurs from world (fall damage/certain killstreaks)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="origin"></param>
|
|
|
|
|
/// <returns></returns>
|
2020-05-04 17:50:02 -04:00
|
|
|
|
private bool IsWorldDamage(EFClient origin) => origin?.NetworkId == Utilities.WORLD_ID || origin?.ClientId == Utilities.WORLD_ID;
|
2019-10-23 11:40:24 -04:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Indicates if we should try to use anticheat even if sv_customcallbacks is not defined
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="s"></param>
|
|
|
|
|
/// <returns></returns>
|
2020-09-30 18:15:47 -04:00
|
|
|
|
private bool ShouldOverrideAnticheatSetting(Server s) => Config.Configuration().AnticheatConfiguration.Enable && s.GameName == Server.Game.IW5;
|
2020-11-18 17:28:14 -05:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Makes sure both clients are added
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="origin"></param>
|
|
|
|
|
/// <param name="target"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private async Task EnsureClientsAdded(EFClient origin, EFClient target)
|
|
|
|
|
{
|
|
|
|
|
await Manager.AddPlayer(origin);
|
|
|
|
|
|
|
|
|
|
if (!origin.Equals(target))
|
|
|
|
|
{
|
|
|
|
|
await Manager.AddPlayer(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-20 17:54:38 -04:00
|
|
|
|
}
|
2018-02-07 00:19:06 -05:00
|
|
|
|
}
|