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
|
class GameEventHandler : IEventHandler
|
||||||
{
|
{
|
||||||
private readonly IManager Manager;
|
|
||||||
static long NextEventId = 1;
|
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)
|
public GameEventHandler(IManager mgr)
|
||||||
{
|
{
|
||||||
Manager = mgr;
|
Manager = mgr;
|
||||||
OutOfOrderEvents = new SortedList<long, GameEvent>();
|
OutOfOrderEvents = new SortedList<long, GameEvent>();
|
||||||
|
IsProcessingEvent = new SemaphoreSlim(0);
|
||||||
|
IsProcessingEvent.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEvent(GameEvent gameEvent)
|
public void AddEvent(GameEvent gameEvent)
|
||||||
@ -45,11 +48,40 @@ namespace IW4MAdmin.Application
|
|||||||
// event occurs
|
// event occurs
|
||||||
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
|
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
|
||||||
{
|
{
|
||||||
#if DEBUG == true
|
//#if DEBUG == true
|
||||||
Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
|
// Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
|
||||||
#endif
|
// 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));
|
((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);
|
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)
|
// 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
|
// offload it to the player to keep
|
||||||
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
|
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
|
||||||
|
newEvent.OnProcessed.Set();
|
||||||
return;
|
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");
|
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
|
// offload it to the player to keep
|
||||||
newEvent.Target.DelayedEvents.Enqueue(newEvent);
|
newEvent.Target.DelayedEvents.Enqueue(newEvent);
|
||||||
|
newEvent.OnProcessed.Set();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,6 @@ namespace IW4MAdmin.Application
|
|||||||
Owner = oldEvent.Owner,
|
Owner = oldEvent.Owner,
|
||||||
Message = oldEvent.Message,
|
Message = oldEvent.Message,
|
||||||
Target = oldEvent.Target,
|
Target = oldEvent.Target,
|
||||||
OnProcessed = oldEvent.OnProcessed,
|
|
||||||
Remote = oldEvent.Remote
|
Remote = oldEvent.Remote
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -257,8 +257,7 @@ namespace IW4MAdmin
|
|||||||
if (cNum >= 0 && Players[cNum] != null)
|
if (cNum >= 0 && Players[cNum] != null)
|
||||||
{
|
{
|
||||||
Player Leaving = Players[cNum];
|
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
|
// occurs when the player disconnects via log before being authenticated by RCon
|
||||||
if (Leaving.State != Player.ClientState.Connected)
|
if (Leaving.State != Player.ClientState.Connected)
|
||||||
{
|
{
|
||||||
@ -267,8 +266,9 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.WriteInfo($"Client {Leaving} [{Leaving.State.ToString().ToLower()}] disconnecting...");
|
||||||
Leaving.State = Player.ClientState.Disconnecting;
|
Leaving.State = Player.ClientState.Disconnecting;
|
||||||
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
Leaving.TotalConnectionTime += Leaving.ConnectionLength;
|
||||||
Leaving.LastConnection = DateTime.UtcNow;
|
Leaving.LastConnection = DateTime.UtcNow;
|
||||||
await Manager.GetClientService().Update(Leaving);
|
await Manager.GetClientService().Update(Leaving);
|
||||||
Players[cNum] = null;
|
Players[cNum] = null;
|
||||||
@ -383,27 +383,29 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Quit)
|
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 &&
|
if (origin != null &&
|
||||||
// // we only want to forward the event if they are connected.
|
// we only want to forward the event if they are connected.
|
||||||
// origin.State == Player.ClientState.Connected)
|
origin.State == Player.ClientState.Connected &&
|
||||||
//{
|
// make sure we don't get the disconnect event from every time the game ends
|
||||||
// var e = new GameEvent()
|
origin.ConnectionLength < Manager.GetApplicationSettings().Configuration().RConPollRate)
|
||||||
// {
|
{
|
||||||
// Type = GameEvent.EventType.Disconnect,
|
var e = new GameEvent()
|
||||||
// Origin = origin,
|
{
|
||||||
// Owner = this
|
Type = GameEvent.EventType.Disconnect,
|
||||||
// };
|
Origin = origin,
|
||||||
|
Owner = this
|
||||||
|
};
|
||||||
|
|
||||||
// Manager.GetEventHandler().AddEvent(e);
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//else if (origin != null &&
|
else if (origin != null &&
|
||||||
// origin.State != Player.ClientState.Connected)
|
origin.State != Player.ClientState.Connected)
|
||||||
//{
|
{
|
||||||
// await RemovePlayer(origin.ClientNumber);
|
await RemovePlayer(origin.ClientNumber);
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||||
@ -781,12 +783,12 @@ namespace IW4MAdmin
|
|||||||
Logger.WriteWarning("Game log file not properly initialized, restarting map...");
|
Logger.WriteWarning("Game log file not properly initialized, restarting map...");
|
||||||
await this.ExecuteCommandAsync("map_restart");
|
await this.ExecuteCommandAsync("map_restart");
|
||||||
logfile = await this.GetDvarAsync<string>("g_log");
|
logfile = await this.GetDvarAsync<string>("g_log");
|
||||||
}
|
}
|
||||||
|
|
||||||
//CustomCallback = await ScriptLoaded();
|
//CustomCallback = await ScriptLoaded();
|
||||||
string mainPath = EventParser.GetGameDir();
|
string mainPath = EventParser.GetGameDir();
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
basepath.Value = @"D:\";
|
// basepath.Value = @"D:\";
|
||||||
#endif
|
#endif
|
||||||
string logPath = string.Empty;
|
string logPath = string.Empty;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ class LogResource(Resource):
|
|||||||
path = urlsafe_b64decode(path).decode('utf-8')
|
path = urlsafe_b64decode(path).decode('utf-8')
|
||||||
log_info = reader.read_file(path)
|
log_info = reader.read_file(path)
|
||||||
|
|
||||||
if not log_info:
|
if log_info is False:
|
||||||
print('could not read log file ' + path)
|
print('could not read log file ' + path)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -42,7 +42,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
|||||||
{
|
{
|
||||||
foreach (string word in objectionalWords)
|
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)
|
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
|
// break out early because there's at least one objectional word
|
||||||
if (containsObjectionalWord)
|
if (containsObjectionalWord)
|
||||||
|
@ -16,6 +16,7 @@ using SharedLibraryCore.Database;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Services;
|
using SharedLibraryCore.Services;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
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 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>
|
/// <summary>
|
||||||
/// gets a ranking across all servers for given client id
|
/// gets a ranking across all servers for given client id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -52,17 +64,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
.Select(r => r.Performance)
|
.Select(r => r.Performance)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
var iqClientRanking = context.Set<EFRating>()
|
||||||
var iqClientRating = (from rating in context.Set<EFRating>()
|
.Where(r => r.RatingHistory.ClientId == clientId)
|
||||||
where rating.RatingHistory.Client.ClientId != clientId
|
.Where(GetRankingFunc());
|
||||||
where rating.ServerId == null
|
|
||||||
where rating.RatingHistory.Client.LastConnection > fifteenDaysAgo
|
return await iqClientRanking.CountAsync() + 1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,28 +76,26 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
{
|
{
|
||||||
using (var context = new DatabaseContext(true))
|
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>()
|
var iqClientRatings = (from rating in context.Set<EFRating>()
|
||||||
where rating.ServerId == null
|
.Where(GetRankingFunc())
|
||||||
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
|
|
||||||
select new
|
select new
|
||||||
{
|
{
|
||||||
Ratings = rating.RatingHistory.Ratings.Where(r => r.ServerId == null),
|
Ratings = rating.RatingHistory.Ratings.Where(r => r.ServerId == null),
|
||||||
rating.RatingHistory.ClientId,
|
rating.RatingHistory.ClientId,
|
||||||
rating.RatingHistory.Client.CurrentAlias.Name,
|
rating.RatingHistory.Client.CurrentAlias.Name,
|
||||||
rating.RatingHistory.Client.LastConnection,
|
rating.RatingHistory.Client.LastConnection,
|
||||||
rating.RatingHistory.Client.TotalConnectionTime,
|
|
||||||
rating.Performance,
|
rating.Performance,
|
||||||
})
|
}).OrderByDescending(c => c.Performance)
|
||||||
.Skip(start)
|
.Skip(start)
|
||||||
.Take(count);
|
.Take(count);
|
||||||
|
#if DEBUG == true
|
||||||
|
var clientRatingsSql = iqClientRatings.ToSql();
|
||||||
|
#endif
|
||||||
|
// materialized list
|
||||||
var clientRatings = await iqClientRatings.ToListAsync();
|
var clientRatings = await iqClientRatings.ToListAsync();
|
||||||
|
|
||||||
|
// get all the client ids that
|
||||||
var clientIds = clientRatings
|
var clientIds = clientRatings
|
||||||
.GroupBy(r => r.ClientId)
|
.GroupBy(r => r.ClientId)
|
||||||
.Select(r => r.First().ClientId)
|
.Select(r => r.First().ClientId)
|
||||||
@ -99,23 +103,19 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
|
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
|
||||||
where clientIds.Contains(stat.ClientId)
|
where clientIds.Contains(stat.ClientId)
|
||||||
|
group stat by stat.ClientId into s
|
||||||
select new
|
select new
|
||||||
{
|
{
|
||||||
stat.ClientId,
|
ClientId = s.Key,
|
||||||
stat.Kills,
|
Kills = s.Sum(c => c.Kills),
|
||||||
stat.Deaths,
|
Deaths = s.Sum(c => c.Deaths),
|
||||||
stat.TimePlayed,
|
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)
|
||||||
});
|
});
|
||||||
|
#if DEBUG == true
|
||||||
var statList = await iqStatsInfo.ToListAsync();
|
var statsInfoSql = iqStatsInfo.ToSql();
|
||||||
var topPlayers = statList.GroupBy(s => s.ClientId)
|
#endif
|
||||||
.Select(s => new
|
var topPlayers = await iqStatsInfo.ToListAsync();
|
||||||
{
|
|
||||||
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)
|
|
||||||
});
|
|
||||||
|
|
||||||
var clientRatingsDict = clientRatings.ToDictionary(r => r.ClientId);
|
var clientRatingsDict = clientRatings.ToDictionary(r => r.ClientId);
|
||||||
var finished = topPlayers.Select(s => new TopStatsInfo()
|
var finished = topPlayers.Select(s => new TopStatsInfo()
|
||||||
@ -131,7 +131,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
PerformanceHistory = clientRatingsDict[s.ClientId].Ratings.Count() > 1 ?
|
PerformanceHistory = clientRatingsDict[s.ClientId].Ratings.Count() > 1 ?
|
||||||
clientRatingsDict[s.ClientId].Ratings.Select(r => r.Performance).ToList() :
|
clientRatingsDict[s.ClientId].Ratings.Select(r => r.Performance).ToList() :
|
||||||
new List<double>() { clientRatingsDict[s.ClientId].Performance, clientRatingsDict[s.ClientId].Performance },
|
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)
|
.OrderByDescending(r => r.Performance)
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -316,6 +316,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// get individual client's stats
|
// get individual client's stats
|
||||||
var clientStats = playerStats[pl.ClientId];
|
var clientStats = playerStats[pl.ClientId];
|
||||||
|
|
||||||
|
#if DEBUG == true
|
||||||
|
await UpdateStatHistory(pl, clientStats);
|
||||||
|
#endif
|
||||||
|
|
||||||
// remove the client from the stats dictionary as they're leaving
|
// remove the client from the stats dictionary as they're leaving
|
||||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
|
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue3);
|
||||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue4);
|
||||||
@ -622,7 +626,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task UpdateStatHistory(Player client, EFClientStatistics clientStats)
|
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())
|
using (var ctx = new DatabaseContext())
|
||||||
{
|
{
|
||||||
@ -651,25 +663,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
Ratings = new List<EFRating>()
|
Ratings = new List<EFRating>()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (clientHistory.RatingHistoryId == 0)
|
#region INDIVIDUAL_SERVER_PERFORMANCE
|
||||||
{
|
|
||||||
ctx.Add(clientHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ctx.Update(clientHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
var fifteenDaysAgo = DateTime.UtcNow.AddDays(-15);
|
||||||
// get the client ranking for the current server
|
// get the client ranking for the current server
|
||||||
int individualClientRanking = await ctx.Set<EFRating>()
|
int individualClientRanking = await ctx.Set<EFRating>()
|
||||||
.Where(c => c.ServerId == clientStats.ServerId)
|
.Where(GetRankingFunc(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(c => c.RatingHistory.ClientId != client.ClientId)
|
.Where(c => c.RatingHistory.ClientId != client.ClientId)
|
||||||
.Where(r => r.Newest)
|
|
||||||
.Where(c => c.Performance > clientStats.Performance)
|
.Where(c => c.Performance > clientStats.Performance)
|
||||||
.CountAsync() + 1;
|
.CountAsync() + 1;
|
||||||
|
|
||||||
@ -682,10 +681,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set the previous newest to false
|
// 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)
|
if (ratingToUnsetNewest != null)
|
||||||
{
|
{
|
||||||
ctx.Entry(ratingToUnsetNewest).State = EntityState.Modified;
|
ctx.Entry(ratingToUnsetNewest).State = EntityState.Modified;
|
||||||
|
ctx.Entry(ratingToUnsetNewest).Property(p => p.Newest).IsModified = true;
|
||||||
ratingToUnsetNewest.Newest = false;
|
ratingToUnsetNewest.Newest = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,9 +699,11 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
Newest = true,
|
Newest = true,
|
||||||
ServerId = clientStats.ServerId,
|
ServerId = clientStats.ServerId,
|
||||||
RatingHistoryId = clientHistory.RatingHistoryId,
|
RatingHistoryId = clientHistory.RatingHistoryId,
|
||||||
ActivityAmount = currentServerTotalPlaytime
|
ActivityAmount = currentServerTotalPlaytime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#region OVERALL_RATING
|
||||||
// get other server stats
|
// get other server stats
|
||||||
var clientStatsList = await iqClientStats.ToListAsync();
|
var clientStatsList = await iqClientStats.ToListAsync();
|
||||||
|
|
||||||
@ -719,28 +722,27 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
int overallClientRanking = await ctx.Set<EFRating>()
|
int overallClientRanking = await ctx.Set<EFRating>()
|
||||||
.Where(r => r.ServerId == null)
|
.Where(GetRankingFunc())
|
||||||
.Where(r => r.RatingHistory.ClientId != client.ClientId)
|
.Where(r => r.RatingHistory.ClientId != client.ClientId)
|
||||||
.Where(r => r.RatingHistory.Client.LastConnection > fifteenDaysAgo)
|
.Where(r => r.Performance > performanceAverage)
|
||||||
.Where(r => r.RatingHistory.Client.Level != Player.Permission.Banned)
|
.CountAsync() + 1;
|
||||||
.Where(r => r.ActivityAmount > Plugin.Config.Configuration().TopPlayersMinPlayTime)
|
|
||||||
.Where(r => r.Newest)
|
|
||||||
.Where(r => r.Performance > performanceAverage)
|
|
||||||
.CountAsync() + 1;
|
|
||||||
|
|
||||||
// limit max average history to 40
|
// limit max average history to 40
|
||||||
if (clientHistory.Ratings.Count(r => r.ServerId == null) >= 40)
|
if (clientHistory.Ratings.Count(r => r.ServerId == null) >= 40)
|
||||||
{
|
{
|
||||||
var ratingToRemove = clientHistory.Ratings.First(r => r.ServerId == null);
|
var ratingToRemove = clientHistory.Ratings.First(r => r.ServerId == null);
|
||||||
|
ctx.Attach(ratingToRemove);
|
||||||
ctx.Entry(ratingToRemove).State = EntityState.Deleted;
|
ctx.Entry(ratingToRemove).State = EntityState.Deleted;
|
||||||
clientHistory.Ratings.Remove(ratingToRemove);
|
clientHistory.Ratings.Remove(ratingToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the previous average newest to false
|
// 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)
|
if (ratingToUnsetNewest != null)
|
||||||
{
|
{
|
||||||
|
ctx.Attach(ratingToUnsetNewest);
|
||||||
ctx.Entry(ratingToUnsetNewest).State = EntityState.Modified;
|
ctx.Entry(ratingToUnsetNewest).State = EntityState.Modified;
|
||||||
|
ctx.Entry(ratingToUnsetNewest).Property(p => p.Newest).IsModified = true;
|
||||||
ratingToUnsetNewest.Newest = false;
|
ratingToUnsetNewest.Newest = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,16 +757,19 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
RatingHistoryId = clientHistory.RatingHistoryId,
|
RatingHistoryId = clientHistory.RatingHistoryId,
|
||||||
ActivityAmount = clientStatsList.Sum(s => s.TimePlayed)
|
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 double EloRating { get; set; }
|
||||||
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
public virtual ICollection<EFHitLocationCount> HitLocations { get; set; }
|
||||||
public double RollingWeightedKDR { get; set; }
|
public double RollingWeightedKDR { get; set; }
|
||||||
|
public double VisionAverage { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double Performance
|
public double Performance
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Models
|
namespace IW4MAdmin.Plugins.Stats.Models
|
||||||
{
|
{
|
||||||
@ -27,5 +25,7 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
public bool Newest { get; set; }
|
public bool Newest { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
public int ActivityAmount { get; set; }
|
public int ActivityAmount { get; set; }
|
||||||
|
[Required]
|
||||||
|
public DateTime When { get; set; } = DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Query;
|
|
||||||
using Microsoft.EntityFrameworkCore.Query.Internal;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WebfrontCore.Controllers;
|
using WebfrontCore.Controllers;
|
||||||
|
|
||||||
@ -23,7 +17,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
|||||||
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_TITLE"];
|
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_TITLE"];
|
||||||
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_DESC"];
|
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]
|
[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/loader.js"></script>
|
||||||
<script type="text/javascript" src="~/js/stats.js"></script>
|
<script type="text/javascript" src="~/js/stats.js"></script>
|
||||||
</environment>
|
</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()
|
public void AddAndRemoveClientsViaJoinShouldSucceed()
|
||||||
{
|
{
|
||||||
var server = Manager.GetServers().First();
|
var server = Manager.GetServers().First();
|
||||||
var waiters = new Queue<SemaphoreSlim>();
|
var waiters = new Queue<GameEvent>();
|
||||||
|
|
||||||
int clientStartIndex = 4;
|
int clientStartIndex = 4;
|
||||||
int clientNum = 10;
|
int clientNum = 10;
|
||||||
@ -63,12 +63,12 @@ namespace Tests
|
|||||||
};
|
};
|
||||||
|
|
||||||
server.Manager.GetEventHandler().AddEvent(e);
|
server.Manager.GetEventHandler().AddEvent(e);
|
||||||
waiters.Enqueue(e.OnProcessed);
|
waiters.Enqueue(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (waiters.Count > 0)
|
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}]");
|
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);
|
server.Manager.GetEventHandler().AddEvent(e);
|
||||||
waiters.Enqueue(e.OnProcessed);
|
waiters.Enqueue(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (waiters.Count > 0)
|
while (waiters.Count > 0)
|
||||||
{
|
{
|
||||||
waiters.Dequeue().Wait();
|
waiters.Dequeue().OnProcessed.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(server.ClientNum == 0, "there are still clients connected");
|
Assert.True(server.ClientNum == 0, "there are still clients connected");
|
||||||
@ -103,7 +103,7 @@ namespace Tests
|
|||||||
public void AddAndRemoveClientsViaRconShouldSucceed()
|
public void AddAndRemoveClientsViaRconShouldSucceed()
|
||||||
{
|
{
|
||||||
var server = Manager.GetServers().First();
|
var server = Manager.GetServers().First();
|
||||||
var waiters = new Queue<SemaphoreSlim>();
|
var waiters = new Queue<GameEvent>();
|
||||||
|
|
||||||
int clientIndexStart = 1;
|
int clientIndexStart = 1;
|
||||||
int clientNum = 8;
|
int clientNum = 8;
|
||||||
@ -126,12 +126,12 @@ namespace Tests
|
|||||||
};
|
};
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
waiters.Enqueue(e.OnProcessed);
|
waiters.Enqueue(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (waiters.Count > 0)
|
while (waiters.Count > 0)
|
||||||
{
|
{
|
||||||
waiters.Dequeue().Wait();
|
waiters.Dequeue().OnProcessed.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
int actualClientNum = server.GetPlayersAsList().Count(p => p.State == Player.ClientState.Connected);
|
int actualClientNum = server.GetPlayersAsList().Count(p => p.State == Player.ClientState.Connected);
|
||||||
@ -155,12 +155,12 @@ namespace Tests
|
|||||||
};
|
};
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
waiters.Enqueue(e.OnProcessed);
|
waiters.Enqueue(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (waiters.Count > 0)
|
while (waiters.Count > 0)
|
||||||
{
|
{
|
||||||
waiters.Dequeue().Wait();
|
waiters.Dequeue().OnProcessed.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
actualClientNum = server.ClientNum;
|
actualClientNum = server.ClientNum;
|
||||||
|
103
README.md
103
README.md
@ -5,14 +5,26 @@ _______
|
|||||||
### About
|
### 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.
|
**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
|
### Download
|
||||||
Latest binary builds are always available at https://raidmax.org/IW4MAdmin
|
Latest binary builds are always available at https://raidmax.org/IW4MAdmin
|
||||||
|
|
||||||
|
---
|
||||||
### Setup
|
### 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*
|
* [.NET Core 2.1 Runtime](https://www.microsoft.com/net/download) *or newer*
|
||||||
1. Extract `IW4MAdmin-<version>.zip`
|
#### Installation
|
||||||
2. Run `StartIW4MAdmin.cmd`
|
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
|
### 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)
|
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.
|
When **IW4MAdmin** is launched for the _first time_, you will be prompted to setup your configuration.
|
||||||
|
|
||||||
`Enable webfront`
|
`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`
|
`Enable multiple owners`
|
||||||
* Enables more than one client to be promoted to level of `Owner`
|
* Enables more than one client to be promoted to level of `Owner`
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
`Enable stepped privilege hierarchy`
|
`Enable stepped privilege hierarchy`
|
||||||
* Allows privileged clients to promote other clients to the level below their current level
|
* Allows privileged clients to promote other clients to the level below their current level
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
`Enable custom say name`
|
`Enable custom say name`
|
||||||
* Shows a prefix to every message send by **IW4MAdmin** -- `[Admin] message`
|
* Shows a prefix to every message send by **IW4MAdmin** -- `[Admin] message`
|
||||||
* _This feature requires you specify a custom say name_
|
* _This feature requires you specify a custom say name_
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
`Enable social link`
|
`Enable social link`
|
||||||
* Shows a link to your community's social media/website on the webfront
|
* Shows a link to your community's social media/website on the webfront
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
`Use Custom Encoding Parser`
|
`Use Custom Encoding Parser`
|
||||||
* Allows alternative encodings to be used for parsing game information and events
|
* 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**
|
* **Russian users should use this and then specify** `windows-1251` **as the encoding string**
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
#### Server Configuration
|
#### Server Configuration
|
||||||
After initial configuration is finished, you will be prompted to configure your servers for **IW4MAdmin**.
|
After initial configuration is finished, you will be prompted to configure your servers for **IW4MAdmin**.
|
||||||
|
|
||||||
`Enter server IP Address`
|
`Enter server IP Address`
|
||||||
* For almost all scenarios `127.0.0.1` is sufficient
|
* For almost all scenarios `127.0.0.1` is sufficient
|
||||||
|
* Default — `n/a`
|
||||||
|
|
||||||
`Enter server port`
|
`Enter server port`
|
||||||
* The port that your server is listening on (can be obtained via `net_port`)
|
* The port that your server is listening on (can be obtained via `net_port`)
|
||||||
|
* Default — `n/a`
|
||||||
|
|
||||||
`Enter server RCon password`
|
`Enter server RCon password`
|
||||||
* The *\(R\)emote (Con)sole* password set in your server configuration (can be obtained via `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`
|
`Use Pluto T6 parser`
|
||||||
* Used if setting up a server for Plutonium T6 (BO2)
|
* Used if setting up a server for Plutonium T6 (BO2)
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
`Use Pluto IW5 parser`
|
`Use Pluto IW5 parser`
|
||||||
* Used if setting a server for Plutonium IW5 (MW3)
|
* Used if setting a server for Plutonium IW5 (MW3)
|
||||||
|
* Default — `false`
|
||||||
|
|
||||||
`Enter number of reserved slots`
|
`Enter number of reserved slots`
|
||||||
* The number of client slots reserver for privileged players (unavailable for regular users to occupy)
|
* The number of client slots reserver for privileged players (unavailable for regular users to occupy)
|
||||||
|
* Default — `0`
|
||||||
|
|
||||||
#### Advanced Configuration
|
#### 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.
|
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.
|
* 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
|
* 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
|
* Example http://gameserver.com:8080
|
||||||
|
* Default — `http://0.0.0.0:1624`
|
||||||
|
|
||||||
`CustomLocale`
|
`CustomLocale`
|
||||||
* Specifies a [locale name](https://msdn.microsoft.com/en-us/library/39cwe7zf.aspx) to use instead of system default
|
* 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
|
* Locale must be from the `Equivalent Locale Name` column
|
||||||
|
* Default — `windows-1252`
|
||||||
|
|
||||||
`ConnectionString`
|
`ConnectionString`
|
||||||
* Specifies the [connection string](https://www.connectionstrings.com/mysql/) to a MySQL server that is used instead of SQLite
|
* Specifies the [connection string](https://www.connectionstrings.com/mysql/) to a MySQL server that is used instead of SQLite
|
||||||
|
* Default — `null`
|
||||||
|
|
||||||
`RConPollRate`
|
`RConPollRate`
|
||||||
* Specifies (in milliseconds) how often to poll each server for updates
|
* Specifies (in milliseconds) how often to poll each server for updates
|
||||||
|
* Default — `5000`
|
||||||
|
|
||||||
`Servers`
|
`Servers`
|
||||||
* Specifies the list of servers **IW4MAdmin** will monitor
|
* Specifies the list of servers **IW4MAdmin** will monitor
|
||||||
|
* Default — `[]`
|
||||||
* `IPAddress`
|
* `IPAddress`
|
||||||
* Specifies the IP Address of the particular server
|
* Specifies the IP Address of the particular server
|
||||||
|
* Default — `n/a`
|
||||||
* `Port`
|
* `Port`
|
||||||
* Specifies the port of the particular server
|
* Specifies the port of the particular server
|
||||||
|
* Default — `n/a`
|
||||||
* `Password`
|
* `Password`
|
||||||
* Specifies the `rcon_password` of the particular server
|
* 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`
|
* `AutoMessages`
|
||||||
* Specifies the list of messages that are broadcasted to the particular server
|
* Specifies the list of messages that are broadcasted to the particular server
|
||||||
|
* Default — `null`
|
||||||
* `Rules`
|
* `Rules`
|
||||||
* Specifies the list of rules that apply to the particular server
|
* Specifies the list of rules that apply to the particular server
|
||||||
|
* Default — `null`
|
||||||
* `ReservedSlotNumber`
|
* `ReservedSlotNumber`
|
||||||
* Specifies the number of client slots to reserve for privileged users
|
* Specifies the number of client slots to reserve for privileged users
|
||||||
|
* Default — `0`
|
||||||
|
|
||||||
`AutoMessagePeriod`
|
`AutoMessagePeriod`
|
||||||
* Specifies (in seconds) how often messages should be broadcasted to each server
|
* Specifies (in seconds) how often messages should be broadcasted to each server
|
||||||
|
* Default — `60`
|
||||||
|
|
||||||
`AutoMessages`
|
`AutoMessages`
|
||||||
* Specifies the list of messages that are broadcasted to **all** servers
|
* 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`
|
- 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
|
- 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
|
- This plugin provides additional integration to IW4x
|
||||||
- In order to take advantage of it, copy the `userraw` folder into your IW4x server directory
|
- 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
|
- This plugin detects if a client is using a VPN and kicks them if they are
|
||||||
- To disable this plugin, delete `Plugins\VPNDetection.js`
|
- To disable this plugin, delete `Plugins\VPNDetection.js`
|
||||||
___
|
___
|
||||||
@ -282,6 +323,39 @@ ___
|
|||||||
`Web Console`
|
`Web Console`
|
||||||
* Allows logged in privileged users to execute commands as if they are in-game
|
* 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
|
### Extending Plugins
|
||||||
#### Code
|
#### Code
|
||||||
IW4Madmin functionality can be extended by writing additional plugins in C#.
|
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
|
Example https://discordapp.com/api/webhooks/id/token
|
||||||
- `DiscordWebhookInformationUrl` — [optional] Discord generated URL to send information to; this includes information such as player messages
|
- `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
|
- `NotifyRoleIds` — [optional] List of [discord role ids](https://discordhelp.net/role-id) to mention when notification hook is sent
|
||||||
#### Launching
|
#### Launching
|
||||||
With Python installed, open a terminal/command prompt window open in the `Webhook` folder and execute `python DiscordWebhook.py`
|
With Python installed, open a terminal/command prompt window open in the `Webhook` folder and execute:
|
||||||
|
```console
|
||||||
|
python DiscordWebhook.py
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
## Misc
|
### Misc
|
||||||
#### Anti-cheat
|
#### 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.
|
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.
|
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.
|
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
|
#### 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.
|
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;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using static SharedLibraryCore.RCon.StaticHelpers;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Commands
|
namespace SharedLibraryCore.Commands
|
||||||
{
|
{
|
||||||
@ -312,6 +313,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
if (P == null)
|
if (P == null)
|
||||||
continue;
|
continue;
|
||||||
// todo: fix spacing
|
// todo: fix spacing
|
||||||
|
// todo: make this better :)
|
||||||
if (P.Masked)
|
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));
|
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
|
else
|
||||||
@ -320,6 +322,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
if (count == 2 || E.Owner.GetPlayersAsList().Count == 1)
|
if (count == 2 || E.Owner.GetPlayersAsList().Count == 1)
|
||||||
{
|
{
|
||||||
await E.Origin.Tell(playerList.ToString());
|
await E.Origin.Tell(playerList.ToString());
|
||||||
|
await Task.Delay(FloodProtectionInterval);
|
||||||
count = 0;
|
count = 0;
|
||||||
playerList = new StringBuilder();
|
playerList = new StringBuilder();
|
||||||
continue;
|
continue;
|
||||||
@ -327,6 +330,11 @@ namespace SharedLibraryCore.Commands
|
|||||||
|
|
||||||
count++;
|
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("[^3" + C.Name + "^7] " + C.Description);
|
||||||
await E.Origin.Tell(C.Syntax);
|
await E.Origin.Tell(C.Syntax);
|
||||||
|
await Task.Delay(FloodProtectionInterval);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -382,6 +391,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
await E.Owner.Broadcast(helpResponse.ToString());
|
await E.Owner.Broadcast(helpResponse.ToString());
|
||||||
else
|
else
|
||||||
await E.Origin.Tell(helpResponse.ToString());
|
await E.Origin.Tell(helpResponse.ToString());
|
||||||
|
await Task.Delay(FloodProtectionInterval);
|
||||||
helpResponse = new StringBuilder();
|
helpResponse = new StringBuilder();
|
||||||
count = 0;
|
count = 0;
|
||||||
}
|
}
|
||||||
@ -564,10 +574,10 @@ namespace SharedLibraryCore.Commands
|
|||||||
{
|
{
|
||||||
foreach (string line in OnlineAdmins(E.Owner).Split(Environment.NewLine))
|
foreach (string line in OnlineAdmins(E.Owner).Split(Environment.NewLine))
|
||||||
{
|
{
|
||||||
if (E.Message[0] == '@')
|
var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
|
||||||
await E.Owner.Broadcast(line);
|
await t;
|
||||||
else
|
|
||||||
await E.Origin.Tell(line);
|
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)}" :
|
$"[^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)}";
|
$"({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 E.Origin.Tell(msg);
|
||||||
|
await Task.Delay(FloodProtectionInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,10 +686,9 @@ namespace SharedLibraryCore.Commands
|
|||||||
|
|
||||||
foreach (string r in rules)
|
foreach (string r in rules)
|
||||||
{
|
{
|
||||||
if (E.Message.IsBroadcastCommand())
|
var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
|
||||||
await E.Owner.Broadcast($"- {r}");
|
await t;
|
||||||
else
|
await Task.Delay(FloodProtectionInterval);
|
||||||
await E.Origin.Tell($"- {r}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -927,7 +937,10 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (Report R in E.Owner.Reports)
|
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 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)
|
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 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(DbContextOptions<DatabaseContext> opt) : base(opt) { }
|
||||||
|
|
||||||
public DatabaseContext(bool disableTracking = false)
|
public DatabaseContext() { }
|
||||||
|
|
||||||
|
public DatabaseContext(bool disableTracking)
|
||||||
{
|
{
|
||||||
if (disableTracking)
|
if (disableTracking)
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@ namespace SharedLibraryCore
|
|||||||
|
|
||||||
public GameEvent()
|
public GameEvent()
|
||||||
{
|
{
|
||||||
OnProcessed = new ManualResetEventSlim();
|
OnProcessed = new ManualResetEventSlim(false);
|
||||||
Time = DateTime.UtcNow;
|
Time = DateTime.UtcNow;
|
||||||
Id = GetNextEventId();
|
Id = GetNextEventId();
|
||||||
}
|
}
|
||||||
@ -105,5 +105,7 @@ namespace SharedLibraryCore
|
|||||||
queuedEvent.Target.State != Player.ClientState.Connected &&
|
queuedEvent.Target.State != Player.ClientState.Connected &&
|
||||||
queuedEvent.Target.NetworkId != 0;
|
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<int>("TimePlayed");
|
||||||
|
|
||||||
|
b.Property<double>("VisionAverage");
|
||||||
|
|
||||||
b.HasKey("ClientId", "ServerId");
|
b.HasKey("ClientId", "ServerId");
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
b.HasIndex("ServerId");
|
||||||
@ -254,6 +256,8 @@ namespace SharedLibraryCore.Migrations
|
|||||||
|
|
||||||
b.Property<int?>("ServerId");
|
b.Property<int?>("ServerId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("When");
|
||||||
|
|
||||||
b.HasKey("RatingId");
|
b.HasKey("RatingId");
|
||||||
|
|
||||||
b.HasIndex("RatingHistoryId");
|
b.HasIndex("RatingHistoryId");
|
||||||
@ -475,6 +479,8 @@ namespace SharedLibraryCore.Migrations
|
|||||||
|
|
||||||
b.HasKey("Vector3Id");
|
b.HasKey("Vector3Id");
|
||||||
|
|
||||||
|
b.HasIndex("EFACSnapshotSnapshotId");
|
||||||
|
|
||||||
b.ToTable("Vector3");
|
b.ToTable("Vector3");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,6 +146,8 @@ namespace SharedLibraryCore.Objects
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public DateTime ConnectionTime { get; set; }
|
public DateTime ConnectionTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
|
public int ConnectionLength => (int)(DateTime.UtcNow - ConnectionTime).TotalSeconds;
|
||||||
|
[NotMapped]
|
||||||
public Server CurrentServer { get; set; }
|
public Server CurrentServer { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int Score { get; set; }
|
public int Score { get; set; }
|
||||||
|
@ -35,7 +35,6 @@ namespace SharedLibraryCore.RCon
|
|||||||
ILogger Log;
|
ILogger Log;
|
||||||
int FailedSends;
|
int FailedSends;
|
||||||
int FailedReceives;
|
int FailedReceives;
|
||||||
static DateTime LastQuery;
|
|
||||||
string response;
|
string response;
|
||||||
|
|
||||||
ManualResetEvent OnConnected;
|
ManualResetEvent OnConnected;
|
||||||
@ -142,14 +141,14 @@ namespace SharedLibraryCore.RCon
|
|||||||
|
|
||||||
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
|
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
|
||||||
{
|
{
|
||||||
// will this really prevent flooding?
|
//// will this really prevent flooding?
|
||||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
//if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
||||||
{
|
//{
|
||||||
Thread.Sleep(350);
|
// Thread.Sleep(350);
|
||||||
//await Task.Delay(350);
|
// //await Task.Delay(350);
|
||||||
}
|
//}
|
||||||
|
|
||||||
LastQuery = DateTime.Now;
|
// LastQuery = DateTime.Now;
|
||||||
|
|
||||||
OnSent.Reset();
|
OnSent.Reset();
|
||||||
OnReceived.Reset();
|
OnReceived.Reset();
|
||||||
|
@ -4,15 +4,45 @@ namespace SharedLibraryCore.RCon
|
|||||||
{
|
{
|
||||||
public static class StaticHelpers
|
public static class StaticHelpers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// defines the type of RCon query sent to a server
|
||||||
|
/// </summary>
|
||||||
public enum QueryType
|
public enum QueryType
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// retrieve the status of a server
|
||||||
|
/// does not require RCon password
|
||||||
|
/// </summary>
|
||||||
GET_STATUS,
|
GET_STATUS,
|
||||||
|
/// <summary>
|
||||||
|
/// retrieve the information of a server
|
||||||
|
/// server responds with key/value pairs
|
||||||
|
/// RCon password is required
|
||||||
|
/// </summary>
|
||||||
GET_INFO,
|
GET_INFO,
|
||||||
|
/// <summary>
|
||||||
|
/// retrieve the value of a DVAR
|
||||||
|
/// RCon password is required
|
||||||
|
/// </summary>
|
||||||
DVAR,
|
DVAR,
|
||||||
|
/// <summary>
|
||||||
|
/// execute a command
|
||||||
|
/// RCon password is required
|
||||||
|
/// </summary>
|
||||||
COMMAND,
|
COMMAND,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// line seperator char included in response from the server
|
||||||
|
/// </summary>
|
||||||
public static char SeperatorChar = (char)int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
|
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);
|
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>
|
<ItemGroup>
|
||||||
<PackageReference Include="Jint" Version="2.11.58" />
|
<PackageReference Include="Jint" Version="2.11.58" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="2.1.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0-preview1-35029" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<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" />
|
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@ using System.Threading.Tasks;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
|
using Microsoft.EntityFrameworkCore.Query.Internal;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
namespace SharedLibraryCore
|
||||||
{
|
{
|
||||||
public static class Utilities
|
public static class Utilities
|
||||||
@ -494,5 +498,34 @@ namespace SharedLibraryCore
|
|||||||
var response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO);
|
var response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO);
|
||||||
return response.FirstOrDefault(r => r[0] == '\\')?.DictionaryFromKeyValue();
|
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,
|
player.Ping,
|
||||||
State = player.State.ToString(),
|
State = player.State.ToString(),
|
||||||
player.ClientNumber,
|
player.ClientNumber,
|
||||||
ConnectionTime = Math.Round((DateTime.UtcNow - player.ConnectionTime).TotalSeconds, 0),
|
ConnectionTime = player.ConnectionLength,
|
||||||
Level = player.Level.ToLocalizedLevelName(),
|
Level = player.Level.ToLocalizedLevelName(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -43,7 +43,7 @@ if ($(loaderResponseId).length === 1) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
$('html').bind('mousewheel DOMMouseScroll', function (e) {
|
$('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) {
|
if (delta < 0 && !hasScrollBar) {
|
||||||
loadMoreItems();
|
loadMoreItems();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user