update readme
add vision average to client stats other stuff
This commit is contained in:
parent
385879618d
commit
ba5b1e19a6
@ -9,14 +9,17 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
class GameEventHandler : IEventHandler
|
||||
{
|
||||
private readonly IManager Manager;
|
||||
static long NextEventId = 1;
|
||||
private readonly SortedList<long, GameEvent> OutOfOrderEvents;
|
||||
readonly IManager Manager;
|
||||
readonly SortedList<long, GameEvent> OutOfOrderEvents;
|
||||
readonly SemaphoreSlim IsProcessingEvent;
|
||||
|
||||
public GameEventHandler(IManager mgr)
|
||||
{
|
||||
Manager = mgr;
|
||||
OutOfOrderEvents = new SortedList<long, GameEvent>();
|
||||
IsProcessingEvent = new SemaphoreSlim(0);
|
||||
IsProcessingEvent.Release();
|
||||
}
|
||||
|
||||
public void AddEvent(GameEvent gameEvent)
|
||||
@ -45,11 +48,40 @@ namespace IW4MAdmin.Application
|
||||
// event occurs
|
||||
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
|
||||
{
|
||||
#if DEBUG == true
|
||||
Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
|
||||
#endif
|
||||
//#if DEBUG == true
|
||||
// Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
|
||||
// IsProcessingEvent.Wait();
|
||||
//#else
|
||||
// if (GameEvent.IsEventTimeSensitive(gameEvent) &&
|
||||
// !IsProcessingEvent.Wait(30 * 1000))
|
||||
// {
|
||||
// Manager.GetLogger().WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_TIMEOUT"]} [{gameEvent.Id}, {gameEvent.Type}]");
|
||||
// }
|
||||
//#endif
|
||||
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
|
||||
|
||||
//if (GameEvent.IsEventTimeSensitive(gameEvent))
|
||||
//{
|
||||
// if( !gameEvent.OnProcessed.Wait(30 * 1000))
|
||||
// {
|
||||
// Manager.GetLogger().WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EVENT_TIMEOUT"]} [{gameEvent.Id}, {gameEvent.Type}]");
|
||||
// }
|
||||
//}
|
||||
Interlocked.Increment(ref NextEventId);
|
||||
//#if DEBUG == true
|
||||
// gameEvent.OnProcessed.Wait();
|
||||
//#else
|
||||
// if (GameEvent.IsEventTimeSensitive(gameEvent) &&
|
||||
// !gameEvent.OnProcessed.Wait(30 * 1000))
|
||||
// {
|
||||
// Manager.GetLogger().WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EVENT_TIMEOUT"]} [{gameEvent.Id}, {gameEvent.Type}]");
|
||||
// }
|
||||
//#endif
|
||||
// Interlocked.Increment(ref NextEventId);
|
||||
// if (GameEvent.IsEventTimeSensitive(gameEvent))
|
||||
// {
|
||||
// IsProcessingEvent.Release();
|
||||
// }
|
||||
}
|
||||
|
||||
// a "newer" event has been added before and "older" one has been added (due to threads and context switching)
|
||||
|
@ -88,6 +88,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
// offload it to the player to keep
|
||||
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
|
||||
newEvent.OnProcessed.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -97,6 +98,7 @@ namespace IW4MAdmin.Application
|
||||
Logger.WriteDebug($"Delaying target execution of event type {newEvent.Type} for {newEvent.Target} because they are not authed");
|
||||
// offload it to the player to keep
|
||||
newEvent.Target.DelayedEvents.Enqueue(newEvent);
|
||||
newEvent.OnProcessed.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -120,7 +122,6 @@ namespace IW4MAdmin.Application
|
||||
Owner = oldEvent.Owner,
|
||||
Message = oldEvent.Message,
|
||||
Target = oldEvent.Target,
|
||||
OnProcessed = oldEvent.OnProcessed,
|
||||
Remote = oldEvent.Remote
|
||||
};
|
||||
|
||||
|
@ -257,8 +257,7 @@ namespace IW4MAdmin
|
||||
if (cNum >= 0 && Players[cNum] != null)
|
||||
{
|
||||
Player Leaving = Players[cNum];
|
||||
Logger.WriteInfo($"Client {Leaving}, state {Leaving.State.ToString()} disconnecting...");
|
||||
|
||||
|
||||
// occurs when the player disconnects via log before being authenticated by RCon
|
||||
if (Leaving.State != Player.ClientState.Connected)
|
||||
{
|
||||
@ -267,8 +266,9 @@ namespace IW4MAdmin
|
||||
|
||||
else
|
||||
{
|
||||
Logger.WriteInfo($"Client {Leaving} [{Leaving.State.ToString().ToLower()}] disconnecting...");
|
||||
Leaving.State = Player.ClientState.Disconnecting;
|
||||
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
||||
Leaving.TotalConnectionTime += Leaving.ConnectionLength;
|
||||
Leaving.LastConnection = DateTime.UtcNow;
|
||||
await Manager.GetClientService().Update(Leaving);
|
||||
Players[cNum] = null;
|
||||
@ -383,27 +383,29 @@ namespace IW4MAdmin
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Quit)
|
||||
{
|
||||
//var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId);
|
||||
var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId);
|
||||
|
||||
//if (origin != null &&
|
||||
// // we only want to forward the event if they are connected.
|
||||
// origin.State == Player.ClientState.Connected)
|
||||
//{
|
||||
// var e = new GameEvent()
|
||||
// {
|
||||
// Type = GameEvent.EventType.Disconnect,
|
||||
// Origin = origin,
|
||||
// Owner = this
|
||||
// };
|
||||
if (origin != null &&
|
||||
// we only want to forward the event if they are connected.
|
||||
origin.State == Player.ClientState.Connected &&
|
||||
// make sure we don't get the disconnect event from every time the game ends
|
||||
origin.ConnectionLength < Manager.GetApplicationSettings().Configuration().RConPollRate)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Disconnect,
|
||||
Origin = origin,
|
||||
Owner = this
|
||||
};
|
||||
|
||||
// Manager.GetEventHandler().AddEvent(e);
|
||||
//}
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
|
||||
//else if (origin != null &&
|
||||
// origin.State != Player.ClientState.Connected)
|
||||
//{
|
||||
// await RemovePlayer(origin.ClientNumber);
|
||||
//}
|
||||
else if (origin != null &&
|
||||
origin.State != Player.ClientState.Connected)
|
||||
{
|
||||
await RemovePlayer(origin.ClientNumber);
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||
@ -781,12 +783,12 @@ namespace IW4MAdmin
|
||||
Logger.WriteWarning("Game log file not properly initialized, restarting map...");
|
||||
await this.ExecuteCommandAsync("map_restart");
|
||||
logfile = await this.GetDvarAsync<string>("g_log");
|
||||
}
|
||||
}
|
||||
|
||||
//CustomCallback = await ScriptLoaded();
|
||||
string mainPath = EventParser.GetGameDir();
|
||||
#if DEBUG
|
||||
basepath.Value = @"D:\";
|
||||
// basepath.Value = @"D:\";
|
||||
#endif
|
||||
string logPath = string.Empty;
|
||||
|
||||
|
@ -7,7 +7,7 @@ class LogResource(Resource):
|
||||
path = urlsafe_b64decode(path).decode('utf-8')
|
||||
log_info = reader.read_file(path)
|
||||
|
||||
if not log_info:
|
||||
if log_info is False:
|
||||
print('could not read log file ' + path)
|
||||
|
||||
return {
|
||||
|
@ -42,7 +42,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
{
|
||||
foreach (string word in objectionalWords)
|
||||
{
|
||||
containsObjectionalWord |= Regex.IsMatch(E.Origin.Name.ToLower(), word);
|
||||
containsObjectionalWord |= Regex.IsMatch(E.Origin.Name.ToLower(), word, RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
|
||||
foreach (string word in objectionalWords)
|
||||
{
|
||||
containsObjectionalWord |= Regex.IsMatch(E.Origin.Name.ToLower(), word);
|
||||
containsObjectionalWord |= Regex.IsMatch(E.Origin.Name.ToLower(), word, RegexOptions.IgnoreCase);
|
||||
|
||||
// break out early because there's at least one objectional word
|
||||
if (containsObjectionalWord)
|
||||
|
@ -16,6 +16,7 @@ using SharedLibraryCore.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Services;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
@ -36,6 +37,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
public EFClientStatistics GetClientStats(int clientId, int serverId) => Servers[serverId].PlayerStats[clientId];
|
||||
|
||||
public Expression<Func<EFRating, bool>> GetRankingFunc(int? serverId = null)
|
||||
{
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
return (r) => r.ServerId == serverId &&
|
||||
r.RatingHistory.Client.LastConnection > fifteenDaysAgo &&
|
||||
r.RatingHistory.Client.Level != Player.Permission.Banned &&
|
||||
r.Newest &&
|
||||
r.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// gets a ranking across all servers for given client id
|
||||
/// </summary>
|
||||
@ -52,17 +64,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
.Select(r => r.Performance)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
var iqClientRating = (from rating in context.Set<EFRating>()
|
||||
where rating.RatingHistory.Client.ClientId != clientId
|
||||
where rating.ServerId == null
|
||||
where rating.RatingHistory.Client.LastConnection > fifteenDaysAgo
|
||||
where rating.RatingHistory.Client.Level != Player.Permission.Banned
|
||||
where rating.Newest
|
||||
where rating.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime
|
||||
where rating.Performance > clientPerformance
|
||||
select rating.Ranking);
|
||||
return await iqClientRating.CountAsync() + 1;
|
||||
var iqClientRanking = context.Set<EFRating>()
|
||||
.Where(r => r.RatingHistory.ClientId == clientId)
|
||||
.Where(GetRankingFunc());
|
||||
|
||||
return await iqClientRanking.CountAsync() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,28 +76,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
// setup the query for the clients within the given rating range
|
||||
var iqClientRatings = (from rating in context.Set<EFRating>()
|
||||
where rating.ServerId == null
|
||||
where rating.RatingHistory.Client.LastConnection > fifteenDaysAgo
|
||||
where rating.RatingHistory.Client.Level != Player.Permission.Banned
|
||||
where rating.Newest
|
||||
where rating.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime
|
||||
orderby rating.Performance descending
|
||||
.Where(GetRankingFunc())
|
||||
select new
|
||||
{
|
||||
Ratings = rating.RatingHistory.Ratings.Where(r => r.ServerId == null),
|
||||
rating.RatingHistory.ClientId,
|
||||
rating.RatingHistory.Client.CurrentAlias.Name,
|
||||
rating.RatingHistory.Client.LastConnection,
|
||||
rating.RatingHistory.Client.TotalConnectionTime,
|
||||
rating.Performance,
|
||||
})
|
||||
.Skip(start)
|
||||
.Take(count);
|
||||
|
||||
}).OrderByDescending(c => c.Performance)
|
||||
.Skip(start)
|
||||
.Take(count);
|
||||
#if DEBUG == true
|
||||
var clientRatingsSql = iqClientRatings.ToSql();
|
||||
#endif
|
||||
// materialized list
|
||||
var clientRatings = await iqClientRatings.ToListAsync();
|
||||
|
||||
// get all the client ids that
|
||||
var clientIds = clientRatings
|
||||
.GroupBy(r => r.ClientId)
|
||||
.Select(r => r.First().ClientId)
|
||||
@ -99,23 +103,19 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
|
||||
where clientIds.Contains(stat.ClientId)
|
||||
group stat by stat.ClientId into s
|
||||
select new
|
||||
{
|
||||
stat.ClientId,
|
||||
stat.Kills,
|
||||
stat.Deaths,
|
||||
stat.TimePlayed,
|
||||
ClientId = s.Key,
|
||||
Kills = s.Sum(c => c.Kills),
|
||||
Deaths = s.Sum(c => c.Deaths),
|
||||
KDR = s.Sum(c => (c.Kills / (double)(c.Deaths == 0 ? 1 : c.Deaths)) * c.TimePlayed) / s.Sum(c => c.TimePlayed),
|
||||
TotalTimePlayed = s.Sum(c => c.TimePlayed)
|
||||
});
|
||||
|
||||
var statList = await iqStatsInfo.ToListAsync();
|
||||
var topPlayers = statList.GroupBy(s => s.ClientId)
|
||||
.Select(s => new
|
||||
{
|
||||
s.First().ClientId,
|
||||
Kills = s.Sum(c => c.Kills),
|
||||
Deaths = s.Sum(c => c.Deaths),
|
||||
KDR = s.Sum(c => (c.Kills / (double)(c.Deaths == 0 ? 1 : c.Deaths)) * c.TimePlayed) / s.Sum(c => c.TimePlayed)
|
||||
});
|
||||
#if DEBUG == true
|
||||
var statsInfoSql = iqStatsInfo.ToSql();
|
||||
#endif
|
||||
var topPlayers = await iqStatsInfo.ToListAsync();
|
||||
|
||||
var clientRatingsDict = clientRatings.ToDictionary(r => r.ClientId);
|
||||
var finished = topPlayers.Select(s => new TopStatsInfo()
|
||||
@ -131,7 +131,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
PerformanceHistory = clientRatingsDict[s.ClientId].Ratings.Count() > 1 ?
|
||||
clientRatingsDict[s.ClientId].Ratings.Select(r => r.Performance).ToList() :
|
||||
new List<double>() { clientRatingsDict[s.ClientId].Performance, clientRatingsDict[s.ClientId].Performance },
|
||||
TimePlayed = Math.Round(clientRatingsDict[s.ClientId].TotalConnectionTime / 3600.0, 1).ToString("#,##0"),
|
||||
TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"),
|
||||
})
|
||||
.OrderByDescending(r => r.Performance)
|
||||
.ToList();
|
||||
@ -316,6 +316,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
// get individual client's stats
|
||||
var clientStats = playerStats[pl.ClientId];
|
||||
|
||||
#if DEBUG == true
|
||||
await UpdateStatHistory(pl, clientStats);
|
||||
#endif
|
||||
|
||||
// remove the client from the stats dictionary as they're leaving
|
||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
||||
@ -622,7 +626,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
/// <returns></returns>
|
||||
private async Task UpdateStatHistory(Player client, EFClientStatistics clientStats)
|
||||
{
|
||||
int currentServerTotalPlaytime = clientStats.TimePlayed + (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds;
|
||||
int currentSessionTime = (int)(DateTime.UtcNow - client.LastConnection).TotalSeconds;
|
||||
|
||||
// don't update their stat history if they haven't played long
|
||||
if (currentSessionTime < 60)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int currentServerTotalPlaytime = clientStats.TimePlayed + currentSessionTime;
|
||||
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
@ -651,25 +663,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Ratings = new List<EFRating>()
|
||||
};
|
||||
|
||||
if (clientHistory.RatingHistoryId == 0)
|
||||
{
|
||||
ctx.Add(clientHistory);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ctx.Update(clientHistory);
|
||||
}
|
||||
|
||||
#region INDIVIDUAL_SERVER_PERFORMANCE
|
||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||
// get the client ranking for the current server
|
||||
int individualClientRanking = await ctx.Set<EFRating>()
|
||||
.Where(c => c.ServerId == clientStats.ServerId)
|
||||
.Where(r => r.RatingHistory.Client.LastConnection > fifteenDaysAgo)
|
||||
.Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned)
|
||||
.Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime)
|
||||
.Where(GetRankingFunc(clientStats.ServerId))
|
||||
.Where(c => c.RatingHistory.ClientId != client.ClientId)
|
||||
.Where(r => r.Newest)
|
||||
.Where(c => c.Performance > clientStats.Performance)
|
||||
.CountAsync() + 1;
|
||||
|
||||
@ -682,10 +681,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// set the previous newest to false
|
||||
var ratingToUnsetNewest = clientHistory.Ratings.LastOrDefault(r => r.ServerId == clientStats.ServerId);
|
||||
var ratingToUnsetNewest = clientHistory.Ratings.LastOrDefault(r => r.ServerId == clientStats.ServerId && r.Newest);
|
||||
|
||||
if (ratingToUnsetNewest != null)
|
||||
{
|
||||
ctx.Entry(ratingToUnsetNewest).State = EntityState.Modified;
|
||||
ctx.Entry(ratingToUnsetNewest).Property(p => p.Newest).IsModified = true;
|
||||
ratingToUnsetNewest.Newest = false;
|
||||
}
|
||||
|
||||
@ -698,9 +699,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Newest = true,
|
||||
ServerId = clientStats.ServerId,
|
||||
RatingHistoryId = clientHistory.RatingHistoryId,
|
||||
ActivityAmount = currentServerTotalPlaytime
|
||||
ActivityAmount = currentServerTotalPlaytime,
|
||||
});
|
||||
|
||||
#endregion
|
||||
#region OVERALL_RATING
|
||||
// get other server stats
|
||||
var clientStatsList = await iqClientStats.ToListAsync();
|
||||
|
||||
@ -719,28 +722,27 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
int overallClientRanking = await ctx.Set<EFRating>()
|
||||
.Where(r => r.ServerId == null)
|
||||
.Where(GetRankingFunc())
|
||||
.Where(r => r.RatingHistory.ClientId != client.ClientId)
|
||||
.Where(r => r.RatingHistory.Client.LastConnection > fifteenDaysAgo)
|
||||
.Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned)
|
||||
.Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime)
|
||||
.Where(r => r.Newest)
|
||||
.Where(r => r.Performance > performanceAverage)
|
||||
.CountAsync() + 1;
|
||||
.Where(r => r.Performance > performanceAverage)
|
||||
.CountAsync() + 1;
|
||||
|
||||
// limit max average history to 40
|
||||
if (clientHistory.Ratings.Count(r => r.ServerId == null) >= 40)
|
||||
{
|
||||
var ratingToRemove = clientHistory.Ratings.First(r => r.ServerId == null);
|
||||
ctx.Attach(ratingToRemove);
|
||||
ctx.Entry(ratingToRemove).State = EntityState.Deleted;
|
||||
clientHistory.Ratings.Remove(ratingToRemove);
|
||||
}
|
||||
|
||||
// set the previous average newest to false
|
||||
ratingToUnsetNewest = clientHistory.Ratings.LastOrDefault(r => r.ServerId == null);
|
||||
ratingToUnsetNewest = clientHistory.Ratings.LastOrDefault(r => r.ServerId == null && r.Newest);
|
||||
if (ratingToUnsetNewest != null)
|
||||
{
|
||||
ctx.Attach(ratingToUnsetNewest);
|
||||
ctx.Entry(ratingToUnsetNewest).State = EntityState.Modified;
|
||||
ctx.Entry(ratingToUnsetNewest).Property(p => p.Newest).IsModified = true;
|
||||
ratingToUnsetNewest.Newest = false;
|
||||
}
|
||||
|
||||
@ -755,16 +757,19 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
RatingHistoryId = clientHistory.RatingHistoryId,
|
||||
ActivityAmount = clientStatsList.Sum(s => s.TimePlayed)
|
||||
});
|
||||
#endregion
|
||||
|
||||
try
|
||||
if (clientHistory.RatingHistoryId == 0)
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
ctx.Add(clientHistory);
|
||||
}
|
||||
// this can happen when the client disconnects without any stat changes
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
|
||||
else
|
||||
{
|
||||
ctx.Update(clientHistory);
|
||||
}
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
public double EloRating { get; set; }
|
||||
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
||||
public double RollingWeightedKDR { get; set; }
|
||||
public double VisionAverage { get; set; }
|
||||
[NotMapped]
|
||||
public double Performance
|
||||
{
|
||||
|
@ -1,9 +1,7 @@
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Models
|
||||
{
|
||||
@ -27,5 +25,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
public bool Newest { get; set; }
|
||||
[Required]
|
||||
public int ActivityAmount { get; set; }
|
||||
[Required]
|
||||
public DateTime When { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Microsoft.EntityFrameworkCore.Query.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using SharedLibraryCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WebfrontCore.Controllers;
|
||||
|
||||
@ -23,7 +17,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_TITLE"];
|
||||
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_DESC"];
|
||||
|
||||
return View("Index", await Plugin.Manager.GetTopStats(0, 10));
|
||||
return View("Index", await Plugin.Manager.GetTopStats(0, 50));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -78,34 +72,4 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG == true
|
||||
public static class IQueryableExtensions
|
||||
{
|
||||
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
|
||||
|
||||
private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");
|
||||
|
||||
private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");
|
||||
|
||||
private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
|
||||
|
||||
private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");
|
||||
|
||||
public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
|
||||
{
|
||||
var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
|
||||
var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
|
||||
var queryModel = modelGenerator.ParseQuery(query.Expression);
|
||||
var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
|
||||
var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
|
||||
var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
|
||||
var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();
|
||||
modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
|
||||
var sql = modelVisitor.Queries.First().ToString();
|
||||
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -10,5 +10,5 @@
|
||||
<script type="text/javascript" src="~/js/loader.js"></script>
|
||||
<script type="text/javascript" src="~/js/stats.js"></script>
|
||||
</environment>
|
||||
<script>initLoader('/Stats/GetTopPlayersAsync', '#stats_top_players');</script>
|
||||
<script>initLoader('/Stats/GetTopPlayersAsync', '#stats_top_players', 50);</script>
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace Tests
|
||||
public void AddAndRemoveClientsViaJoinShouldSucceed()
|
||||
{
|
||||
var server = Manager.GetServers().First();
|
||||
var waiters = new Queue<SemaphoreSlim>();
|
||||
var waiters = new Queue<GameEvent>();
|
||||
|
||||
int clientStartIndex = 4;
|
||||
int clientNum = 10;
|
||||
@ -63,12 +63,12 @@ namespace Tests
|
||||
};
|
||||
|
||||
server.Manager.GetEventHandler().AddEvent(e);
|
||||
waiters.Enqueue(e.OnProcessed);
|
||||
waiters.Enqueue(e);
|
||||
}
|
||||
|
||||
while (waiters.Count > 0)
|
||||
{
|
||||
waiters.Dequeue().Wait();
|
||||
waiters.Dequeue().OnProcessed.Wait();
|
||||
}
|
||||
|
||||
Assert.True(server.ClientNum == clientNum, $"client num does not match added client num [{server.ClientNum}:{clientNum}]");
|
||||
@ -88,12 +88,12 @@ namespace Tests
|
||||
};
|
||||
|
||||
server.Manager.GetEventHandler().AddEvent(e);
|
||||
waiters.Enqueue(e.OnProcessed);
|
||||
waiters.Enqueue(e);
|
||||
}
|
||||
|
||||
while (waiters.Count > 0)
|
||||
{
|
||||
waiters.Dequeue().Wait();
|
||||
waiters.Dequeue().OnProcessed.Wait();
|
||||
}
|
||||
|
||||
Assert.True(server.ClientNum == 0, "there are still clients connected");
|
||||
@ -103,7 +103,7 @@ namespace Tests
|
||||
public void AddAndRemoveClientsViaRconShouldSucceed()
|
||||
{
|
||||
var server = Manager.GetServers().First();
|
||||
var waiters = new Queue<SemaphoreSlim>();
|
||||
var waiters = new Queue<GameEvent>();
|
||||
|
||||
int clientIndexStart = 1;
|
||||
int clientNum = 8;
|
||||
@ -126,12 +126,12 @@ namespace Tests
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
waiters.Enqueue(e.OnProcessed);
|
||||
waiters.Enqueue(e);
|
||||
}
|
||||
|
||||
while (waiters.Count > 0)
|
||||
{
|
||||
waiters.Dequeue().Wait();
|
||||
waiters.Dequeue().OnProcessed.Wait();
|
||||
}
|
||||
|
||||
int actualClientNum = server.GetPlayersAsList().Count(p => p.State == Player.ClientState.Connected);
|
||||
@ -155,12 +155,12 @@ namespace Tests
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
waiters.Enqueue(e.OnProcessed);
|
||||
waiters.Enqueue(e);
|
||||
}
|
||||
|
||||
while (waiters.Count > 0)
|
||||
{
|
||||
waiters.Dequeue().Wait();
|
||||
waiters.Dequeue().OnProcessed.Wait();
|
||||
}
|
||||
|
||||
actualClientNum = server.ClientNum;
|
||||
|
103
README.md
103
README.md
@ -5,14 +5,26 @@ _______
|
||||
### About
|
||||
**IW4MAdmin** is an administration tool for [IW4x](https://iw4xcachep26muba.onion.link/), [Pluto T6](https://forum.plutonium.pw/category/33/plutonium-t6), [Pluto IW5](https://forum.plutonium.pw/category/5/plutonium-iw5), and most Call of Duty® dedicated servers. It allows complete control of your server; from changing maps, to banning players, **IW4MAdmin** monitors and records activity on your server(s). With plugin support, extending its functionality is a breeze.
|
||||
### Download
|
||||
Latest binary builds are always available at https://raidmax.org/IW4MAdmin
|
||||
Latest binary builds are always available at https://raidmax.org/IW4MAdmin
|
||||
|
||||
---
|
||||
### Setup
|
||||
**IW4MAdmin** requires minimal configuration to run. There is only one prerequisite.
|
||||
**IW4MAdmin** requires minimal effort to get up and running.
|
||||
#### Prerequisites
|
||||
* [.NET Core 2.1 Runtime](https://www.microsoft.com/net/download) *or newer*
|
||||
1. Extract `IW4MAdmin-<version>.zip`
|
||||
2. Run `StartIW4MAdmin.cmd`
|
||||
#### Installation
|
||||
1. Install .NET Core Runtime
|
||||
2. Extract `IW4MAdmin-<version>.zip`
|
||||
#### Launching
|
||||
1. Run `StartIW4MAdmin.cmd` (Windows)
|
||||
2. Run `StartIW4MAdmin.sh` (Linux)
|
||||
2. Configure **IW4MAdmin**
|
||||
### Updating
|
||||
1. Extract newer version of **IW4MAdmin** into pre-existing **IW4MAdmin** folder and overwrite existing files
|
||||
- _Your configuration and database will be saved_
|
||||
---
|
||||
### Help
|
||||
Feel free to join the **IW4MAdmin** [Discord](https://discord.gg/ZZFK5p3)
|
||||
Feel free to join the **IW4MAdmin** [Discord](https://discord.gg/ZZFK5p3)
|
||||
If you come across an issue, bug, or feature request please post an [issue](https://github.com/RaidMax/IW4M-Admin/issues)
|
||||
___
|
||||
|
||||
@ -21,45 +33,58 @@ ___
|
||||
When **IW4MAdmin** is launched for the _first time_, you will be prompted to setup your configuration.
|
||||
|
||||
`Enable webfront`
|
||||
* Enables you to monitor and control your server(s) through a web interface [defaults to `http://127.0.0.1:1624`]
|
||||
* Enables you to monitor and control your server(s) through a web interface
|
||||
* Default — `http://0.0.0.0:1624`
|
||||
|
||||
`Enable multiple owners`
|
||||
* Enables more than one client to be promoted to level of `Owner`
|
||||
* Default — `false`
|
||||
|
||||
`Enable stepped privilege hierarchy`
|
||||
* Allows privileged clients to promote other clients to the level below their current level
|
||||
* Default — `false`
|
||||
|
||||
`Enable custom say name`
|
||||
* Shows a prefix to every message send by **IW4MAdmin** -- `[Admin] message`
|
||||
* _This feature requires you specify a custom say name_
|
||||
* Default — `false`
|
||||
|
||||
`Enable social link`
|
||||
* Shows a link to your community's social media/website on the webfront
|
||||
* Default — `false`
|
||||
|
||||
`Use Custom Encoding Parser`
|
||||
* Allows alternative encodings to be used for parsing game information and events
|
||||
* **Russian users should use this and then specify** `windows-1251` **as the encoding string**
|
||||
* Default — `false`
|
||||
|
||||
#### Server Configuration
|
||||
After initial configuration is finished, you will be prompted to configure your servers for **IW4MAdmin**.
|
||||
|
||||
`Enter server IP Address`
|
||||
* For almost all scenarios `127.0.0.1` is sufficient
|
||||
* Default — `n/a`
|
||||
|
||||
`Enter server port`
|
||||
* The port that your server is listening on (can be obtained via `net_port`)
|
||||
* Default — `n/a`
|
||||
|
||||
`Enter server RCon password`
|
||||
* The *\(R\)emote (Con)sole* password set in your server configuration (can be obtained via `rcon_password`)
|
||||
* Default — `n/a`
|
||||
|
||||
`Use Pluto T6 parser`
|
||||
* Used if setting up a server for Plutonium T6 (BO2)
|
||||
* Default — `false`
|
||||
|
||||
`Use Pluto IW5 parser`
|
||||
* Used if setting a server for Plutonium IW5 (MW3)
|
||||
* Default — `false`
|
||||
|
||||
`Enter number of reserved slots`
|
||||
* The number of client slots reserver for privileged players (unavailable for regular users to occupy)
|
||||
* Default — `0`
|
||||
|
||||
#### Advanced Configuration
|
||||
If you wish to further customize your experience of **IW4MAdmin**, the following configuration file(s) will allow you to changes core options using any text-editor.
|
||||
|
||||
@ -70,34 +95,50 @@ If you wish to further customize your experience of **IW4MAdmin**, the following
|
||||
* Specifies the address and port the webfront will listen on.
|
||||
* The value can be an [IP Address](https://en.wikipedia.org/wiki/IP_address):port or [Domain Name](https://en.wikipedia.org/wiki/Domain_name):port
|
||||
* Example http://gameserver.com:8080
|
||||
* Default — `http://0.0.0.0:1624`
|
||||
|
||||
`CustomLocale`
|
||||
* Specifies a [locale name](https://msdn.microsoft.com/en-us/library/39cwe7zf.aspx) to use instead of system default
|
||||
* Locale must be from the `Equivalent Locale Name` column
|
||||
* Default — `windows-1252`
|
||||
|
||||
`ConnectionString`
|
||||
* Specifies the [connection string](https://www.connectionstrings.com/mysql/) to a MySQL server that is used instead of SQLite
|
||||
* Default — `null`
|
||||
|
||||
`RConPollRate`
|
||||
* Specifies (in milliseconds) how often to poll each server for updates
|
||||
* Default — `5000`
|
||||
|
||||
`Servers`
|
||||
* Specifies the list of servers **IW4MAdmin** will monitor
|
||||
* Default — `[]`
|
||||
* `IPAddress`
|
||||
* Specifies the IP Address of the particular server
|
||||
* Default — `n/a`
|
||||
* `Port`
|
||||
* Specifies the port of the particular server
|
||||
* Default — `n/a`
|
||||
* `Password`
|
||||
* Specifies the `rcon_password` of the particular server
|
||||
* Default — `n/a`
|
||||
* `ManualLogPath`
|
||||
* Specifies the log path to be used instead of the automatically generated one
|
||||
* To use the `GameLogServer`, this should be set to the http address that the `GameLogServer` is listening on
|
||||
* Example — http://gamelogserver.com/
|
||||
* `AutoMessages`
|
||||
* Specifies the list of messages that are broadcasted to the particular server
|
||||
* Default — `null`
|
||||
* `Rules`
|
||||
* Specifies the list of rules that apply to the particular server
|
||||
* Default — `null`
|
||||
* `ReservedSlotNumber`
|
||||
* Specifies the number of client slots to reserve for privileged users
|
||||
* Default — `0`
|
||||
|
||||
`AutoMessagePeriod`
|
||||
* Specifies (in seconds) how often messages should be broadcasted to each server
|
||||
* Default — `60`
|
||||
|
||||
`AutoMessages`
|
||||
* Specifies the list of messages that are broadcasted to **all** servers
|
||||
@ -254,11 +295,11 @@ ___
|
||||
- Profane words and warning message can be specified in `ProfanityDetermentSettings.json`
|
||||
- If a client's name contains a word listed in the settings, they will immediately be kicked
|
||||
|
||||
####IW4 Script Commands
|
||||
#### IW4 Script Commands
|
||||
- This plugin provides additional integration to IW4x
|
||||
- In order to take advantage of it, copy the `userraw` folder into your IW4x server directory
|
||||
|
||||
####VPN Detection [Script Plugin]
|
||||
#### VPN Detection [Script Plugin]
|
||||
- This plugin detects if a client is using a VPN and kicks them if they are
|
||||
- To disable this plugin, delete `Plugins\VPNDetection.js`
|
||||
___
|
||||
@ -282,6 +323,39 @@ ___
|
||||
`Web Console`
|
||||
* Allows logged in privileged users to execute commands as if they are in-game
|
||||
---
|
||||
### Game Log Server
|
||||
The game log server provides a way to remotely host your server's log over a http rest api.
|
||||
This server is useful if you plan on running IW4MAdmin on a different machine than the game server
|
||||
#### Requirements
|
||||
- [Python 3.6](https://www.python.org/downloads/) or newer
|
||||
- The following [PIP](https://pypi.org/project/pip/) packages (provided in `requirements.txt`)
|
||||
```Flask>=1.0.2
|
||||
aniso8601>=3.0.2
|
||||
click>=6.7
|
||||
Flask-RESTful>=0.3.6
|
||||
itsdangerous>=0.24
|
||||
Jinja2>=2.10
|
||||
MarkupSafe>=1.0
|
||||
pip>=9.0.3
|
||||
pytz>=2018.5
|
||||
setuptools>=39.0.1
|
||||
six>=1.11.0
|
||||
Werkzeug>=0.14.1
|
||||
```
|
||||
#### Installation
|
||||
1. With Python 3 installed, open up a terminal/command prompt window in the `GameLogServer` folder and execute:
|
||||
```console
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
2. Allow TCP port 1625 through firewall
|
||||
* [Windows Instructions](https://www.tomshardware.com/news/how-to-open-firewall-ports-in-windows-10,36451.html)
|
||||
* [Linux Instructions (iptables)](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-basic-iptables-firewall-on-centos-6#open-up-ports-for-selected-services)
|
||||
#### Launching
|
||||
With Python 3 installed, open a terminal/command prompt window open in the `GameServerLog` folder and execute:
|
||||
```console
|
||||
python runserver.py
|
||||
```
|
||||
---
|
||||
### Extending Plugins
|
||||
#### Code
|
||||
IW4Madmin functionality can be extended by writing additional plugins in C#.
|
||||
@ -347,15 +421,20 @@ Example http://127.0.0.1
|
||||
Example https://discordapp.com/api/webhooks/id/token
|
||||
- `DiscordWebhookInformationUrl` — [optional] Discord generated URL to send information to; this includes information such as player messages
|
||||
- `NotifyRoleIds` — [optional] List of [discord role ids](https://discordhelp.net/role-id) to mention when notification hook is sent
|
||||
#### Launching
|
||||
With Python installed, open a terminal/command prompt window open in the `Webhook` folder and execute `python DiscordWebhook.py`
|
||||
#### Launching
|
||||
With Python installed, open a terminal/command prompt window open in the `Webhook` folder and execute:
|
||||
```console
|
||||
python DiscordWebhook.py
|
||||
```
|
||||
|
||||
---
|
||||
## Misc
|
||||
### Misc
|
||||
#### Anti-cheat
|
||||
This is an [IW4x](https://iw4xcachep26muba.onion.link/) only feature (wider game support planned), that uses analytics to detect aimbots and aim-assist tools.
|
||||
To utilize anti-cheat, enable it during setup **and** copy `_customcallbacks.gsc` from `userraw` into your `IW4x Server\userraw\scripts` folder.
|
||||
The anti-cheat feature is a work in progress and as such will be constantly tweaked and may not be 100% accurate, however the goal is to deter as many cheaters as possible from IW4x.
|
||||
#### Database Storage
|
||||
By default, all **IW4MAdmin** information is stored in `Database.db`. Should you need to reset your database, this file can simply be deleted. Additionally, this file should be preserved during updates to retain client information.
|
||||
By default, all **IW4MAdmin** information is stored in `Database.db`.
|
||||
Should you need to reset your database, this file can simply be deleted.
|
||||
Additionally, this file should be preserved during updates to retain client information.
|
||||
Setting the `ConnectionString` property in `IW4MAdminSettings.json` will cause **IW4MAdmin** to attempt to use a MySQL connection for database storage.
|
@ -11,6 +11,7 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.RCon.StaticHelpers;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
{
|
||||
@ -312,6 +313,7 @@ namespace SharedLibraryCore.Commands
|
||||
if (P == null)
|
||||
continue;
|
||||
// todo: fix spacing
|
||||
// todo: make this better :)
|
||||
if (P.Masked)
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(Player.Permission.User, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces(Player.Permission.SeniorAdmin.ToString().Length - Player.Permission.User.ToString().Length));
|
||||
else
|
||||
@ -320,6 +322,7 @@ namespace SharedLibraryCore.Commands
|
||||
if (count == 2 || E.Owner.GetPlayersAsList().Count == 1)
|
||||
{
|
||||
await E.Origin.Tell(playerList.ToString());
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
count = 0;
|
||||
playerList = new StringBuilder();
|
||||
continue;
|
||||
@ -327,6 +330,11 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
if (playerList.Length > 0)
|
||||
{
|
||||
await E.Origin.Tell(playerList.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,6 +365,7 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
await E.Origin.Tell("[^3" + C.Name + "^7] " + C.Description);
|
||||
await E.Origin.Tell(C.Syntax);
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@ -382,6 +391,7 @@ namespace SharedLibraryCore.Commands
|
||||
await E.Owner.Broadcast(helpResponse.ToString());
|
||||
else
|
||||
await E.Origin.Tell(helpResponse.ToString());
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
helpResponse = new StringBuilder();
|
||||
count = 0;
|
||||
}
|
||||
@ -564,10 +574,10 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
foreach (string line in OnlineAdmins(E.Owner).Split(Environment.NewLine))
|
||||
{
|
||||
if (E.Message[0] == '@')
|
||||
await E.Owner.Broadcast(line);
|
||||
else
|
||||
await E.Origin.Tell(line);
|
||||
var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
|
||||
await t;
|
||||
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -645,6 +655,7 @@ namespace SharedLibraryCore.Commands
|
||||
$"[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level, localizedLevel)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}" :
|
||||
$"({P.AliasLink.Children.First(a => a.Name.ToLower().Contains(E.Data.ToLower())).Name})->[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level, localizedLevel)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}";
|
||||
await E.Origin.Tell(msg);
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -675,10 +686,9 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
foreach (string r in rules)
|
||||
{
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
await E.Owner.Broadcast($"- {r}");
|
||||
else
|
||||
await E.Origin.Tell($"- {r}");
|
||||
var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
|
||||
await t;
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -927,7 +937,10 @@ namespace SharedLibraryCore.Commands
|
||||
}
|
||||
|
||||
foreach (Report R in E.Owner.Reports)
|
||||
{
|
||||
await E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason));
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1054,6 +1067,7 @@ namespace SharedLibraryCore.Commands
|
||||
foreach (var P in Plugins.PluginImporter.ActivePlugins)
|
||||
{
|
||||
await E.Origin.Tell(String.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author));
|
||||
await Task.Delay(FloodProtectionInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt) { }
|
||||
|
||||
public DatabaseContext(bool disableTracking = false)
|
||||
public DatabaseContext() { }
|
||||
|
||||
public DatabaseContext(bool disableTracking)
|
||||
{
|
||||
if (disableTracking)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ namespace SharedLibraryCore
|
||||
|
||||
public GameEvent()
|
||||
{
|
||||
OnProcessed = new ManualResetEventSlim();
|
||||
OnProcessed = new ManualResetEventSlim(false);
|
||||
Time = DateTime.UtcNow;
|
||||
Id = GetNextEventId();
|
||||
}
|
||||
@ -105,5 +105,7 @@ namespace SharedLibraryCore
|
||||
queuedEvent.Target.State != Player.ClientState.Connected &&
|
||||
queuedEvent.Target.NetworkId != 0;
|
||||
}
|
||||
|
||||
public static bool IsEventTimeSensitive(GameEvent gameEvent) => gameEvent.Type == EventType.Connect;
|
||||
}
|
||||
}
|
||||
|
669
SharedLibraryCore/Migrations/20180907020706_AddVision.Designer.cs
generated
Normal file
669
SharedLibraryCore/Migrations/20180907020706_AddVision.Designer.cs
generated
Normal file
@ -0,0 +1,669 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20180907020706_AddVision")]
|
||||
partial class AddVision
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int?>("CurrentViewAngleVector3Id");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int?>("HitDestinationVector3Id");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int?>("HitOriginVector3Id");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int?>("LastStrainAngleVector3Id");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleVector3Id");
|
||||
|
||||
b.HasIndex("HitDestinationVector3Id");
|
||||
|
||||
b.HasIndex("HitOriginVector3Id");
|
||||
|
||||
b.HasIndex("LastStrainAngleVector3Id");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<int?>("ServerId");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime>("Expires");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
23
SharedLibraryCore/Migrations/20180907020706_AddVision.cs
Normal file
23
SharedLibraryCore/Migrations/20180907020706_AddVision.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class AddVision : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<double>(
|
||||
name: "VisionAverage",
|
||||
table: "EFClientStatistics",
|
||||
nullable: false,
|
||||
defaultValue: 0.0);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "VisionAverage",
|
||||
table: "EFClientStatistics");
|
||||
}
|
||||
}
|
||||
}
|
669
SharedLibraryCore/Migrations/20180908004053_AddWhenToRating.Designer.cs
generated
Normal file
669
SharedLibraryCore/Migrations/20180908004053_AddWhenToRating.Designer.cs
generated
Normal file
@ -0,0 +1,669 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20180908004053_AddWhenToRating")]
|
||||
partial class AddWhenToRating
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int?>("CurrentViewAngleVector3Id");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int?>("HitDestinationVector3Id");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int?>("HitOriginVector3Id");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int?>("LastStrainAngleVector3Id");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleVector3Id");
|
||||
|
||||
b.HasIndex("HitDestinationVector3Id");
|
||||
|
||||
b.HasIndex("HitOriginVector3Id");
|
||||
|
||||
b.HasIndex("LastStrainAngleVector3Id");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<int?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime>("Expires");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class AddWhenToRating : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "When",
|
||||
table: "EFRating",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "When",
|
||||
table: "EFRating");
|
||||
}
|
||||
}
|
||||
}
|
@ -198,6 +198,8 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
@ -254,6 +256,8 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<int?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
@ -475,6 +479,8 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
|
@ -146,6 +146,8 @@ namespace SharedLibraryCore.Objects
|
||||
[NotMapped]
|
||||
public DateTime ConnectionTime { get; set; }
|
||||
[NotMapped]
|
||||
public int ConnectionLength => (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds;
|
||||
[NotMapped]
|
||||
public Server CurrentServer { get; set; }
|
||||
[NotMapped]
|
||||
public int Score { get; set; }
|
||||
|
@ -35,7 +35,6 @@ namespace SharedLibraryCore.RCon
|
||||
ILogger Log;
|
||||
int FailedSends;
|
||||
int FailedReceives;
|
||||
static DateTime LastQuery;
|
||||
string response;
|
||||
|
||||
ManualResetEvent OnConnected;
|
||||
@ -142,14 +141,14 @@ namespace SharedLibraryCore.RCon
|
||||
|
||||
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
|
||||
{
|
||||
// will this really prevent flooding?
|
||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
||||
{
|
||||
Thread.Sleep(350);
|
||||
//await Task.Delay(350);
|
||||
}
|
||||
//// will this really prevent flooding?
|
||||
//if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
||||
//{
|
||||
// Thread.Sleep(350);
|
||||
// //await Task.Delay(350);
|
||||
//}
|
||||
|
||||
LastQuery = DateTime.Now;
|
||||
// LastQuery = DateTime.Now;
|
||||
|
||||
OnSent.Reset();
|
||||
OnReceived.Reset();
|
||||
|
@ -4,15 +4,45 @@ namespace SharedLibraryCore.RCon
|
||||
{
|
||||
public static class StaticHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// defines the type of RCon query sent to a server
|
||||
/// </summary>
|
||||
public enum QueryType
|
||||
{
|
||||
/// <summary>
|
||||
/// retrieve the status of a server
|
||||
/// does not require RCon password
|
||||
/// </summary>
|
||||
GET_STATUS,
|
||||
/// <summary>
|
||||
/// retrieve the information of a server
|
||||
/// server responds with key/value pairs
|
||||
/// RCon password is required
|
||||
/// </summary>
|
||||
GET_INFO,
|
||||
/// <summary>
|
||||
/// retrieve the value of a DVAR
|
||||
/// RCon password is required
|
||||
/// </summary>
|
||||
DVAR,
|
||||
/// <summary>
|
||||
/// execute a command
|
||||
/// RCon password is required
|
||||
/// </summary>
|
||||
COMMAND,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// line seperator char included in response from the server
|
||||
/// </summary>
|
||||
public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
|
||||
/// <summary>
|
||||
/// timeout in seconds to wait for a socket send or receive before giving up
|
||||
/// </summary>
|
||||
public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 10);
|
||||
/// <summary>
|
||||
/// interval in milliseconds to wait before sending the next RCon request
|
||||
/// </summary>
|
||||
public static readonly int FloodProtectionInterval = 350;
|
||||
}
|
||||
}
|
||||
|
@ -18,16 +18,16 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jint" Version="2.11.58" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0-preview1-35029" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.1" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
|
||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -12,6 +12,10 @@ using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Microsoft.EntityFrameworkCore.Query.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
public static class Utilities
|
||||
@ -494,5 +498,34 @@ namespace SharedLibraryCore
|
||||
var response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO);
|
||||
return response.FirstOrDefault(r => r[0] == '\\')?.DictionaryFromKeyValue();
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG == true
|
||||
|
||||
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
|
||||
|
||||
private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");
|
||||
|
||||
private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");
|
||||
|
||||
private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
|
||||
|
||||
private static readonly PropertyInfo DatabaseDependenciesField = typeof(Microsoft.EntityFrameworkCore.Storage.Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");
|
||||
|
||||
public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
|
||||
{
|
||||
var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
|
||||
var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
|
||||
var queryModel = modelGenerator.ParseQuery(query.Expression);
|
||||
var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
|
||||
var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
|
||||
var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
|
||||
var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();
|
||||
modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
|
||||
var sql = modelVisitor.Queries.First().ToString();
|
||||
|
||||
return sql;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace WebfrontCore.Controllers.API
|
||||
player.Ping,
|
||||
State = player.State.ToString(),
|
||||
player.ClientNumber,
|
||||
ConnectionTime = Math.Round((DateTime.UtcNow - player.ConnectionTime).TotalSeconds, 0),
|
||||
ConnectionTime = player.ConnectionLength,
|
||||
Level = player.Level.ToLocalizedLevelName(),
|
||||
})
|
||||
});
|
||||
|
@ -43,7 +43,7 @@ if ($(loaderResponseId).length === 1) {
|
||||
*/
|
||||
|
||||
$('html').bind('mousewheel DOMMouseScroll', function (e) {
|
||||
var delta = (e.originalEvent.wheelDelta || -e.originalEvent.detail);
|
||||
var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;
|
||||
|
||||
if (delta < 0 && !hasScrollBar) {
|
||||
loadMoreItems();
|
||||
|
Loading…
Reference in New Issue
Block a user